home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / amiga / plotting / gnuplot3.lzh / gnuplot / command.c < prev    next >
C/C++ Source or Header  |  1991-09-24  |  85KB  |  3,219 lines

  1. /* GNUPLOT - command.c */
  2. /*
  3.  * Copyright (C) 1986, 1987, 1990, 1991   Thomas Williams, Colin Kelley
  4.  *
  5.  * Permission to use, copy, and distribute this software and its
  6.  * documentation for any purpose with or without fee is hereby granted, 
  7.  * provided that the above copyright notice appear in all copies and 
  8.  * that both that copyright notice and this permission notice appear 
  9.  * in supporting documentation.
  10.  *
  11.  * Permission to modify the software is granted, but not the right to
  12.  * distribute the modified code.  Modifications are to be distributed 
  13.  * as patches to released version.
  14.  *  
  15.  * This software is provided "as is" without express or implied warranty.
  16.  * 
  17.  *
  18.  * AUTHORS
  19.  * 
  20.  *   Original Software:
  21.  *     Thomas Williams,  Colin Kelley.
  22.  * 
  23.  *   Gnuplot 2.0 additions:
  24.  *       Russell Lang, Dave Kotz, John Campbell.
  25.  *
  26.  *   Gnuplot 3.0 additions:
  27.  *       Gershon Elber and many others.
  28.  * 
  29.  * Send your comments or suggestions to 
  30.  *  pixar!info-gnuplot@sun.com.
  31.  * This is a mailing list; to join it send a note to 
  32.  *  pixar!info-gnuplot-request@sun.com.  
  33.  * Send bug reports to
  34.  *  pixar!bug-gnuplot@sun.com.
  35.  */
  36.  
  37. #include <stdio.h>
  38. #include <math.h>
  39.  
  40. #ifdef AMIGA_AC_5
  41. #include <time.h>
  42. void sleep();        /* defined later */
  43. #endif
  44.  
  45. #ifdef MSDOS
  46. #include <process.h>
  47.  
  48. #ifdef __ZTC__
  49. #define P_WAIT 0
  50. #include <time.h>    /* usleep() */
  51. #else
  52.  
  53. #ifdef __TURBOC__
  54. #include <dos.h>    /* sleep() */
  55. #include <conio.h>
  56. extern unsigned _stklen = 16394;    /* increase stack size */
  57.  
  58. #else    /* must be MSC */
  59. #include <time.h>    /* kludge to provide sleep() */
  60. void sleep();        /* defined later */
  61. #endif /* TURBOC */
  62. #endif /* ZTC */
  63.  
  64. #endif /* MSDOS */
  65.  
  66. #ifdef AMIGA_LC_5_1
  67. #include <proto/dos.h>
  68. void sleep();
  69. #endif    /* AMIGA_LC_5_1 */
  70.  
  71. #include "plot.h"
  72. #include "setshow.h"
  73. #include "help.h"
  74.  
  75. #ifndef STDOUT
  76. #define STDOUT 1
  77. #endif
  78.  
  79. #ifndef HELPFILE
  80. #ifdef AMIGA_LC_5_1
  81. #define HELPFILE "S:gnuplot.gih"
  82. #else
  83. #define HELPFILE "docs/gnuplot.gih" /* changed by makefile */
  84. #endif
  85. #endif
  86.  
  87. #define inrange(z,min,max) ((min<max) ? ((z>=min)&&(z<=max)) : ((z>=max)&&(z<=min)) )
  88.  
  89. /*
  90.  * instead of <strings.h>
  91.  */
  92.  
  93. extern char *gets(),*getenv();
  94. extern char *strcpy(),*strncpy(),*strcat();
  95. extern int strlen(), strcmp();
  96.  
  97. /*
  98.  * Only reference to contours library.
  99.  */
  100. extern struct gnuplot_contours *contour();
  101.  
  102. #if defined(unix) && !defined(hpux)
  103. #ifdef GETCWD
  104. extern char *getcwd();    /* some Unix's use getcwd */
  105. #else
  106. extern char *getwd();    /* most Unix's use getwd */
  107. #endif
  108. #else
  109. extern char *getcwd();    /* Turbo C, MSC and VMS use getcwd */
  110. #endif
  111.  
  112. #ifdef vms
  113. int vms_vkid; /* Virtual keyboard id */
  114. #endif
  115.  
  116. extern int chdir();
  117.  
  118. extern double magnitude(),angle(),real(),imag();
  119. extern struct value *const_express(), *pop(), *complex();
  120. extern struct at_type *temp_at(), *perm_at();
  121. extern struct udft_entry *add_udf();
  122. extern struct udvt_entry *add_udv();
  123. extern void squash_spaces();
  124. extern void lower_case();
  125.  
  126. /* local functions */
  127. static enum coord_type adjustlog();
  128.  
  129. extern BOOLEAN interactive;    /* from plot.c */
  130.  
  131. /* input data, parsing variables */
  132. struct lexical_unit token[MAX_TOKENS];
  133. char input_line[MAX_LINE_LEN+1] = "";
  134. int num_tokens, c_token;
  135. int inline_num = 0;            /* input line number */
  136.  
  137. char c_dummy_var[MAX_NUM_VAR][MAX_ID_LEN+1];    /* current dummy vars */
  138.  
  139. /* the curves/surfaces of the plot */
  140. struct curve_points *first_plot = NULL;
  141. struct surface_points *first_3dplot = NULL;
  142. static struct udft_entry plot_func;
  143. struct udft_entry *dummy_func;
  144.  
  145. /* support for replot command */
  146. char replot_line[MAX_LINE_LEN+1] = "";
  147. static int plot_token;                    /* start of 'plot' command */
  148.  
  149. /* If last plot was a 3d one. */
  150. BOOLEAN is_3d_plot = FALSE;
  151.  
  152. com_line()
  153. {
  154.     read_line(PROMPT);
  155.  
  156.     /* So we can flag any new output: if false at time of error, */
  157.     /* we reprint the command line before printing caret. */
  158.     /* TRUE for interactive terminals, since the command line is typed. */
  159.     /* FALSE for non-terminal stdin, so command line is printed anyway. */
  160.     /* (DFK 11/89) */
  161.     screen_ok = interactive; 
  162.  
  163.     do_line();
  164. }
  165.  
  166.  
  167. do_line()      /* also used in load_file */
  168. {
  169.     if (is_system(input_line[0])) {
  170.         do_system();
  171.         (void) fputs("!\n",stderr);
  172.         return;
  173.     }
  174.     num_tokens = scanner(input_line);
  175.     c_token = 0;
  176.     while(c_token < num_tokens) {
  177.         command();
  178.         if (c_token < num_tokens)    /* something after command */
  179.             if (equals(c_token,";"))
  180.                 c_token++;
  181.             else
  182.                     int_error("';' expected",c_token);
  183.     }
  184. }
  185.  
  186.  
  187.  
  188. command()
  189. {
  190.     int i;
  191.     char sv_file[MAX_LINE_LEN+1];
  192.     /* string holding name of save or load file */
  193.  
  194.     for (i = 0; i < MAX_NUM_VAR; i++)
  195.         c_dummy_var[i][0] = '\0';        /* no dummy variables */
  196.  
  197.     if (is_definition(c_token))
  198.         define();
  199.     else if (almost_equals(c_token,"h$elp") || equals(c_token,"?")) {
  200.         c_token++;
  201.         do_help();
  202.     }
  203.     else if (almost_equals(c_token,"test")) {
  204.         c_token++;
  205.         test_term();
  206.     }
  207.     else if (almost_equals(c_token,"pa$use")) {
  208.         struct value a;
  209.         int stime, text=0;
  210.         char buf[MAX_LINE_LEN+1];
  211.  
  212.         c_token++;
  213.         stime = (int )real(const_express(&a));
  214.         if (!(END_OF_COMMAND)) {
  215.             if (!isstring(c_token))
  216.                 int_error("expecting string",c_token);
  217.             else {
  218.                 quotel_str(buf,c_token);
  219.                 (void) fprintf (stderr, "%s",buf);
  220.                 text = 1;
  221.             }
  222.         }
  223.         if (stime < 0) (void) fgets (buf,MAX_LINE_LEN,stdin);  
  224.                         /* Hold until CR hit. */
  225. #ifdef __ZTC__
  226.         if (stime > 0) usleep((unsigned long) stime);
  227. #else
  228.         if (stime > 0) sleep((unsigned int) stime);
  229. #endif
  230.         if (text != 0 && stime >= 0) (void) fprintf (stderr,"\n");
  231.         c_token++;
  232.         screen_ok = FALSE;
  233.     }
  234.     else if (almost_equals(c_token,"pr$int")) {
  235.         struct value a;
  236.  
  237.         c_token++;
  238.         (void) const_express(&a);
  239.         (void) putc('\t',stderr);
  240.         disp_value(stderr,&a);
  241.         (void) putc('\n',stderr);
  242.         screen_ok = FALSE;
  243.     }
  244.     else if (almost_equals(c_token,"p$lot")) {
  245.         plot_token = c_token++;
  246.         plotrequest();
  247.     }
  248.     else if (almost_equals(c_token,"sp$lot")) {
  249.         plot_token = c_token++;
  250.         plot3drequest();
  251.     }
  252.     else if (almost_equals(c_token,"rep$lot")) {
  253.         if (replot_line[0] == '\0') 
  254.             int_error("no previous plot",c_token);
  255.         c_token++;
  256.         replotrequest();
  257.     }
  258.     else if (almost_equals(c_token,"se$t"))
  259.         set_command();
  260.     else if (almost_equals(c_token,"sh$ow"))
  261.         show_command();
  262.     else if (almost_equals(c_token,"cl$ear")) {
  263.         if (!term_init) {
  264.             (*term_tbl[term].init)();
  265.             term_init = TRUE;
  266.         }
  267.         (*term_tbl[term].graphics)();
  268.         (*term_tbl[term].text)();
  269.         (void) fflush(outfile);
  270.         screen_ok = FALSE;
  271.         c_token++;
  272.     }
  273.     else if (almost_equals(c_token,"she$ll")) {
  274.         do_shell();
  275.         screen_ok = FALSE;
  276.         c_token++;
  277.     }
  278.     else if (almost_equals(c_token,"sa$ve")) {
  279.         if (almost_equals(++c_token,"f$unctions")) {
  280.             if (!isstring(++c_token))
  281.                 int_error("expecting filename",c_token);
  282.             else {
  283.                 quote_str(sv_file,c_token);
  284.                 save_functions(fopen(sv_file,"w"));
  285.             }
  286.         }
  287.         else if (almost_equals(c_token,"v$ariables")) {
  288.             if (!isstring(++c_token))
  289.                 int_error("expecting filename",c_token);
  290.             else {
  291.                 quote_str(sv_file,c_token);
  292.                 save_variables(fopen(sv_file,"w"));
  293.             }
  294.         }
  295.         else if (almost_equals(c_token,"s$et")) {
  296.             if (!isstring(++c_token))
  297.                 int_error("expecting filename",c_token);
  298.             else {
  299.                 quote_str(sv_file,c_token);
  300.                 save_set(fopen(sv_file,"w"));
  301.             }
  302.         }
  303.         else if (isstring(c_token)) {
  304.             quote_str(sv_file,c_token);
  305.             save_all(fopen(sv_file,"w"));
  306.         }
  307.         else {
  308.             int_error(
  309.         "filename or keyword 'functions', 'variables', or 'set' expected",
  310.                     c_token);
  311.         }
  312.         c_token++;
  313.     }
  314.     else if (almost_equals(c_token,"l$oad")) {
  315.         if (!isstring(++c_token))
  316.             int_error("expecting filename",c_token);
  317.         else {
  318.             quote_str(sv_file,c_token);
  319.             load_file(fopen(sv_file,"r"), sv_file);    
  320.         /* input_line[] and token[] now destroyed! */
  321.             c_token = num_tokens = 0;
  322.         }
  323.     }
  324.     else if (almost_equals(c_token,"cd")) {
  325.         if (!isstring(++c_token))
  326.             int_error("expecting directory name",c_token);
  327.         else {
  328.             quotel_str(sv_file,c_token);
  329.             if (chdir(sv_file)) {
  330.               int_error("Can't change to this directory",c_token);
  331.             }
  332.         c_token++;
  333.         }
  334.     }
  335.     else if (almost_equals(c_token,"pwd")) {
  336. #if defined(unix) && !defined(hpux)
  337. #ifdef GETCWD
  338.       (void) getcwd(sv_file,MAX_ID_LEN); /* some Unix's use getcwd */
  339. #else
  340.       (void) getwd(sv_file); /* most Unix's use getwd */
  341. #endif
  342. #else
  343. /* Turbo C and VMS have getcwd() */
  344.       (void) getcwd(sv_file,MAX_ID_LEN);
  345. #endif
  346.       fprintf(stderr,"%s\n", sv_file);
  347.       c_token++;
  348.     }
  349.     else if (almost_equals(c_token,"ex$it") ||
  350.             almost_equals(c_token,"q$uit")) {
  351.         done(IO_SUCCESS);
  352.     }
  353.     else if (!equals(c_token,";")) {        /* null statement */
  354.         int_error("invalid command",c_token);
  355.     }
  356. }
  357.  
  358. replotrequest()
  359. {
  360. char str[MAX_LINE_LEN+1];
  361.         if(equals(c_token,"["))
  362.             int_error("cannot set range with replot",c_token);
  363.         if (!END_OF_COMMAND) {
  364.             capture(str,c_token,num_tokens-1);
  365.             if ( (strlen(str) + strlen(replot_line)) <= MAX_LINE_LEN-1) {
  366.                 (void) strcat(replot_line,",");
  367.                 (void) strcat(replot_line,str); 
  368.             } else {
  369.                 int_error("plot line too long with replot arguments",c_token);
  370.             }
  371.         }
  372.         (void) strcpy(input_line,replot_line);
  373.         screen_ok = FALSE;
  374.         num_tokens = scanner(input_line);
  375.         c_token = 1;                    /* skip the 'plot' part */
  376.         is_3d_plot ? plot3drequest() : plotrequest();
  377. }
  378.  
  379.  
  380. plotrequest()
  381. /*
  382.    In the parametric case we can say 
  383.       plot [a= -4:4] [-2:2] [-1:1] sin(a),a**2
  384.    while in the non-parametric case we would say only
  385.       plot [b= -2:2] [-1:1] sin(b)
  386. */
  387. {
  388.     BOOLEAN changed;
  389.     int dummy_token = -1;
  390.  
  391.     is_3d_plot = FALSE;
  392.  
  393.     if (parametric && strcmp(dummy_var[0], "u") == 0)
  394.     strcpy (dummy_var[0], "t");
  395.  
  396.     autoscale_lt = autoscale_t;
  397.     autoscale_lx = autoscale_x;
  398.     autoscale_ly = autoscale_y;
  399.  
  400.     if (!term)                    /* unknown */
  401.         int_error("use 'set term' to set terminal type first",c_token);
  402.  
  403.     if (equals(c_token,"[")) {
  404.         c_token++;
  405.         if (isletter(c_token)) {
  406.             if (equals(c_token+1,"=")) {
  407.                dummy_token = c_token;
  408.                c_token += 2;
  409.             } else {
  410.                /* oops; probably an expression with a variable. */
  411.                /* Parse it as an xmin expression. */
  412.                /* used to be: int_error("'=' expected",c_token); */
  413.             }
  414.         }
  415.         changed = parametric ? load_range(&tmin,&tmax):load_range(&xmin,&xmax);
  416.         if (!equals(c_token,"]"))
  417.             int_error("']' expected",c_token);
  418.         c_token++;
  419.         if (changed) {
  420.             if (parametric)
  421.                 autoscale_lt = FALSE;
  422.             else
  423.                 autoscale_lx = FALSE;
  424.         }
  425.     }
  426.  
  427.     if (parametric && equals(c_token,"[")) { /* set optional x ranges */
  428.         c_token++;
  429.         changed = load_range(&xmin,&xmax);
  430.         if (!equals(c_token,"]"))
  431.             int_error("']' expected",c_token);
  432.         c_token++;
  433.         if (changed)
  434.           autoscale_lx = FALSE;
  435.     }
  436.  
  437.     if (equals(c_token,"[")) { /* set optional y ranges */
  438.         c_token++;
  439.         changed = load_range(&ymin,&ymax);
  440.         if (!equals(c_token,"]"))
  441.             int_error("']' expected",c_token);
  442.         c_token++;
  443.         if (changed)
  444.           autoscale_ly = FALSE;
  445.     }
  446.  
  447.      /* use the default dummy variable unless changed */
  448.     if (dummy_token >= 0)
  449.       copy_str(c_dummy_var[0],dummy_token);
  450.     else
  451.       (void) strcpy(c_dummy_var[0],dummy_var[0]);
  452.  
  453.     eval_plots();
  454. }
  455.  
  456. plot3drequest()
  457. /*
  458.    in the parametric case we would say
  459.       splot [u= -Pi:Pi] [v= 0:2*Pi] [-1:1] [-1:1] [-1:1] sin(v)*cos(u),sin(v)*cos(u),sin(u)
  460.    in the non-parametric case we would say only
  461.       splot [x= -2:2] [y= -5:5] sin(x)*cos(y)
  462.  
  463. */
  464. {
  465.     BOOLEAN changed;
  466.     int dummy_token0 = -1,
  467.     dummy_token1 = -1;
  468.  
  469.     is_3d_plot = TRUE;
  470.  
  471.     if (parametric && strcmp(dummy_var[0], "t") == 0) {
  472.         strcpy (dummy_var[0], "u");
  473.         strcpy (dummy_var[1], "v");
  474.     }
  475.  
  476.     autoscale_lx = autoscale_x;
  477.     autoscale_ly = autoscale_y;
  478.     autoscale_lz = autoscale_z;
  479.  
  480.     if (!term)                    /* unknown */
  481.         int_error("use 'set term' to set terminal type first",c_token);
  482.  
  483.     if (equals(c_token,"[")) {
  484.         c_token++;
  485.         if (isletter(c_token)) {
  486.             if (equals(c_token+1,"=")) {
  487.                dummy_token0 = c_token;
  488.                c_token += 2;
  489.             } else {
  490.                /* oops; probably an expression with a variable. */
  491.                /* Parse it as an xmin expression. */
  492.                /* used to be: int_error("'=' expected",c_token); */
  493.             }
  494.         }
  495.         changed = parametric ? load_range(&umin,&umax):load_range(&xmin,&xmax);
  496.         if (!equals(c_token,"]"))
  497.             int_error("']' expected",c_token);
  498.         c_token++;
  499.         if (changed && !parametric) {
  500.             autoscale_lx = FALSE;
  501.         }
  502.     }
  503.  
  504.     if (equals(c_token,"[")) {
  505.         c_token++;
  506.         if (isletter(c_token)) {
  507.             if (equals(c_token+1,"=")) {
  508.                dummy_token1 = c_token;
  509.                c_token += 2;
  510.             } else {
  511.                /* oops; probably an expression with a variable. */
  512.                /* Parse it as an xmin expression. */
  513.                /* used to be: int_error("'=' expected",c_token); */
  514.             }
  515.         }
  516.         changed = parametric ? load_range(&vmin,&vmax):load_range(&ymin,&ymax);
  517.         if (!equals(c_token,"]"))
  518.             int_error("']' expected",c_token);
  519.         c_token++;
  520.         if (changed && !parametric) {
  521.             autoscale_ly = FALSE;
  522.         }
  523.     }
  524.  
  525.     if (equals(c_token,"[")) { /* set optional x ranges */
  526.         c_token++;
  527.         changed = load_range(&xmin,&xmax);
  528.         if (!equals(c_token,"]"))
  529.             int_error("']' expected",c_token);
  530.         c_token++;
  531.         if (changed)
  532.           autoscale_lx = FALSE;
  533.     }
  534.  
  535.     if (equals(c_token,"[")) { /* set optional y ranges */
  536.         c_token++;
  537.         changed = load_range(&ymin,&ymax);
  538.         if (!equals(c_token,"]"))
  539.             int_error("']' expected",c_token);
  540.         c_token++;
  541.         if (changed)
  542.           autoscale_ly = FALSE;
  543.     }
  544.  
  545.     if (equals(c_token,"[")) { /* set optional z ranges */
  546.         c_token++;
  547.         changed = load_range(&zmin,&zmax);
  548.         if (!equals(c_token,"]"))
  549.             int_error("']' expected",c_token);
  550.         c_token++;
  551.         if (changed)
  552.           autoscale_lz = FALSE;
  553.     }
  554.  
  555.      /* use the default dummy variable unless changed */
  556.     if (dummy_token0 >= 0)
  557.       copy_str(c_dummy_var[0],dummy_token0);
  558.     else
  559.       (void) strcpy(c_dummy_var[0],dummy_var[0]);
  560.  
  561.     if (dummy_token1 >= 0)
  562.       copy_str(c_dummy_var[1],dummy_token1);
  563.     else
  564.       (void) strcpy(c_dummy_var[1],dummy_var[1]);
  565.  
  566.     eval_3dplots();
  567. }
  568.  
  569.  
  570. define()
  571. {
  572. register int start_token;  /* the 1st token in the function definition */
  573. register struct udvt_entry *udv;
  574. register struct udft_entry *udf;
  575.  
  576.     if (equals(c_token+1,"(")) {
  577.         /* function ! */
  578.         start_token = c_token;
  579.         copy_str(c_dummy_var[0], c_token + 2);
  580.         if(equals(c_token+3,",")) {
  581.             copy_str(c_dummy_var[1], c_token + 4);
  582.             c_token += 2;  /* skip the  , dummy2 */
  583.         }
  584.         c_token += 5; /* skip (, dummy, ) and = */
  585.         if (END_OF_COMMAND)
  586.             int_error("function definition expected",c_token);
  587.         udf = dummy_func = add_udf(start_token);
  588.         if (udf->at)                /* already a dynamic a.t. there */
  589.             free((char *)udf->at);    /* so free it first */
  590.         if ((udf->at = perm_at()) == (struct at_type *)NULL)
  591.             int_error("not enough memory for function",start_token);
  592.         m_capture(&(udf->definition),start_token,c_token-1);
  593.     }
  594.     else {
  595.         /* variable ! */
  596.         start_token = c_token;
  597.         c_token +=2;
  598.         udv = add_udv(start_token);
  599.         (void) const_express(&(udv->udv_value));
  600.         udv->udv_undef = FALSE;
  601.     }
  602. }
  603.  
  604.  
  605. get_data(this_plot)
  606. struct curve_points *this_plot;
  607. {
  608. register int i, l_num, datum;
  609. register FILE *fp;
  610. float x, y;
  611. float ylow, yhigh;            /* for error bars */
  612. float temp;
  613. BOOLEAN yfirst;
  614. char format[MAX_LINE_LEN+1], data_file[MAX_LINE_LEN+1], line[MAX_LINE_LEN+1];
  615. char *float_format = "%f", *float_skip = "%*f";
  616. int xcol = 1, ycol = 2, yemin = 3, yemax = 4;
  617. struct value colvalue;
  618. #ifdef AMIGA_LC_5_1
  619. int num_perc_ast;
  620. char *start_search;
  621. #endif /* AMIGA_LC_5_1 */
  622.  
  623.     quote_str(data_file, c_token);
  624.     this_plot->plot_type = DATA;
  625.     if ((fp = fopen(data_file, "r")) == (FILE *)NULL)
  626.         os_error("can't open data file", c_token);
  627.  
  628.     format[0] = '\0';
  629.     yfirst = FALSE;
  630.     c_token++;    /* skip data file name */
  631.     if (almost_equals(c_token,"u$sing")) {
  632.         c_token++;      /* skip "using" */
  633.         if (!END_OF_COMMAND && !isstring(c_token)) {
  634.             struct value a;
  635.             ycol = (int)magnitude(const_express(&a));
  636.             if (equals(c_token,":")) {
  637.                 c_token++;      /* skip ":" */
  638.                 xcol = ycol;
  639.                 ycol = (int)magnitude(const_express(&a));
  640.                 if (equals(c_token,":")) {
  641.                     c_token++;      /* skip ":" */
  642.                     yemin = (int)magnitude(const_express(&a));
  643.                     if (equals(c_token,":")) {
  644.                         c_token++;      /* skip ":" */
  645.                         yemax = (int)magnitude(const_express(&a));
  646.                     }
  647.                     else
  648.                             yemax = -1;
  649.                 }
  650.                 else {
  651.                         yemin = -1;
  652.                         yemax = -1;
  653.                 }
  654.             }
  655.             else {
  656.                     yemin = -1;
  657.                     yemax = -1;
  658.             }
  659.             if (xcol > ycol) yfirst = TRUE;
  660.         }
  661.         if (!END_OF_COMMAND && isstring(c_token)) {
  662.             quotel_str(format, c_token);
  663.             c_token++;    /* skip format */
  664.         }
  665.     }
  666.     if (strlen(format) == 0) {
  667.         for(i = 1; i <= ((xcol > ycol) ? xcol : ycol); i++)
  668.             if ((i == xcol) || (i == ycol))
  669.                 (void) strcat(format,float_format);
  670.             else
  671.                 (void) strcat(format,float_skip);
  672.  
  673.         if (yemin > 0) {
  674.             /* We have error bars - handle them. */
  675.             yemin -= (xcol > ycol) ? xcol : ycol;
  676.             yemax -= (xcol > ycol) ? xcol : ycol;
  677.             if (yemin > yemax && yemax > 0) {
  678.                 i = yemin;
  679.                 yemin = yemax;
  680.                 yemax = i;
  681.             }
  682.  
  683.             if (yemin == yemax)
  684.                 int_error("Two error bar columns are the same",
  685.                       NO_CARET);
  686.  
  687.             if (yemin <= 0)
  688.                 int_error("Error bar columns must follow data columns",
  689.                       NO_CARET);
  690.  
  691.             for (i = 1; i < yemin; i++)
  692.                 (void) strcat(format,float_skip);
  693.             (void) strcat(format,float_format);
  694.  
  695.             if (yemax > 0) {
  696.                 for (i = 1; i < yemax - yemin; i++)
  697.                     (void) strcat(format,float_skip);
  698.                 (void) strcat(format,float_format);
  699.             }
  700.         }
  701.     }
  702.  
  703.     l_num = 0;
  704.      datum = 0;
  705.     i = 0;
  706. #ifdef AMIGA_LC_5_1
  707.     num_perc_ast = 0;
  708.     start_search = format;
  709.     while (*start_search != '\0') {
  710.         if (start_search[0] == '%')
  711.             if (start_search[1] == '*') num_perc_ast++;
  712.         start_search++;
  713.     }
  714. #endif /* AMIGA_LC_5_1 */
  715.     while ( fgets(line, MAX_LINE_LEN, fp) != (char *)NULL ) {
  716.         l_num++;
  717.         if (is_comment(line[0]))
  718.             continue;        /* ignore comments */
  719.         if (i >= this_plot->p_max) {
  720.             /* overflow about to occur. Extend size of points[]
  721.             * array. We either double the size, or add 1000 points,
  722.             * whichever is a smaller increment. Note i=p_max.
  723.             */
  724.             cp_extend(this_plot, i + (i < 1000 ? i : 1000));
  725.         }
  726.         if (!line[1]) { /* is it blank line ? */
  727.             /* break in data, make next point undefined */
  728.             this_plot->points[i].type = UNDEFINED;
  729.             i++;
  730.             continue;
  731.         }
  732.  
  733. #ifdef AMIGA_LC_5_1
  734.         switch (sscanf(line, format, &x, &y, &ylow, &yhigh) -
  735.              num_perc_ast) {
  736. #else /* AMIGA_LC_5_1 */
  737.         switch (sscanf(line, format, &x, &y, &ylow, &yhigh)) {
  738. #endif /* AMIGA_LC_5_1 */
  739.             case 1: {        /* only one number on the line */
  740.                y = x;        /* assign that number to y */
  741.                x = datum;    /* and make the index into x */
  742.                /* no break; !!! */
  743.             }
  744.             case 2: {
  745.                if (yfirst) { /* exchange x and y */
  746.                   temp = y;
  747.                   y = x;
  748.                   x = temp;
  749.                }
  750.                datum++;
  751.  
  752.                /* ylow and yhigh are same as y */
  753.                store2d_point(this_plot, i++, x, y, y, y);
  754.                break;
  755.             }
  756.             case 3: {        /* x, y, ydelta */
  757.                if (yfirst) { /* exchange x and y */
  758.                   temp = y;
  759.                   y = x;
  760.                   x = temp;
  761.                }
  762.                datum++;
  763.  
  764.                /* ydelta is in the ylow variable */
  765.                store2d_point(this_plot, i++, x, y, y-ylow, y+ylow);
  766.                break;
  767.             }
  768.             case 4: {        /* x, y, ylow, yhigh */
  769.                if (yfirst) { /* exchange x and y */
  770.                   temp = y;
  771.                   y = x;
  772.                   x = temp;
  773.                }
  774.                datum++;
  775.  
  776.                store2d_point(this_plot, i++, x, y, ylow, yhigh);
  777.                break;
  778.             }
  779.             default: {
  780.                (void) sprintf(line, "bad data on line %d", l_num);
  781.                int_error(line,c_token);
  782.             }
  783.         }
  784.     }
  785.     this_plot->p_count = i;
  786.     cp_extend(this_plot, i);    /* shrink to fit */
  787.     (void) fclose(fp);
  788. }
  789.  
  790. /* called by get_data for each point */
  791. store2d_point(this_plot, i, x, y, ylow, yhigh)
  792. struct curve_points *this_plot;
  793. int i;                    /* point number */
  794. #ifdef AMIGA_LC_5_1
  795. double x, y;
  796. double ylow, yhigh;
  797. #else
  798. float x, y;
  799. float ylow, yhigh;
  800. #endif
  801. {
  802.     struct coordinate *cp = &(this_plot->points[i]);
  803.  
  804.     /* the easy part: */
  805.     cp->type = INRANGE;
  806.     cp->x = x;
  807.     cp->y = y;
  808.     cp->ylow = ylow;
  809.     cp->yhigh = yhigh;
  810.     
  811.     /* Adjust for log scale. */
  812.     if (log_x)
  813.      cp->type = adjustlog(cp->type, &(cp->x));
  814.     if (log_y) {
  815.        cp->type = adjustlog(cp->type, &(cp->y));
  816.        /* Note ylow,yhigh can't affect cp->type. */
  817.        (void)     adjustlog(cp->type, &(cp->ylow));
  818.        (void)     adjustlog(cp->type, &(cp->yhigh));
  819.     }
  820.  
  821.     /* Now adjust the xrange, or declare the point out of range */
  822.     /* The yrange is handled later, once we know whether to 
  823.     * include ylow, yhigh in the calculation. See adjust_yrange()
  824.     */
  825.     if (cp->type == INRANGE)
  826.      if (autoscale_lx || inrange(x,xmin,xmax)) {
  827.         if (autoscale_lx) {
  828.             if (x < xmin) xmin = x;
  829.             if (x > xmax) xmax = x;
  830.         }
  831.      } else {
  832.         cp->type = OUTRANGE;
  833.      }
  834. }
  835.  
  836. /* Adjust for log scale:
  837.  * take the log of the second parameter, in place, if possible. 
  838.  * If not possible, return new type for point; if possible, then 
  839.  * return old type for point.
  840.  */
  841. static enum coord_type
  842. adjustlog(type, val)
  843.     enum coord_type type;
  844.     coordval *val;
  845. {
  846.     if (*val < 0.0) {
  847.        return(UNDEFINED);
  848.     } else if (*val == 0.0) {
  849.        *val = -VERYLARGE;
  850.        return(OUTRANGE);
  851.     } else {
  852.        *val = log10(*val);
  853.        return(type);
  854.     }
  855. }
  856.  
  857.  
  858. /* now adjust the yrange, or declare the point out of range */
  859. /* this does all points in a curve */
  860. adjust_yrange(curve)
  861.     struct curve_points *curve;
  862. {
  863.     BOOLEAN ebars = (curve->plot_style == ERRORBARS);
  864.     int npoints = curve->p_count; /* number of points */
  865.     coordval y, ylow, yhigh;    /* one point value */
  866.     struct coordinate *cp;    /* one coordinate */
  867.     int i;                /* index into points */
  868.  
  869.     for (i = 0; i < npoints; i++) {
  870.        cp = &(curve->points[i]);
  871.        if (cp->type == INRANGE) {
  872.           y = cp->y;
  873.           if ((autoscale_ly ||
  874.                inrange(log_y ? pow(10.0, y) : y, ymin, ymax) ||
  875.                polar)) {
  876.              if (autoscale_ly) {
  877.                 if (y < ymin) ymin = y;
  878.                 if (y > ymax) ymax = y;
  879.                 if (ebars) {
  880.                     ylow = cp->ylow;
  881.                     yhigh = cp->yhigh;
  882.                     if (ylow < ymin) ymin = ylow;
  883.                     if (ylow > ymax) ymax = ylow;
  884.                     if (yhigh < ymin) ymin = yhigh;
  885.                     if (yhigh > ymax) ymax = yhigh;
  886.                 }
  887.              }
  888.           } else {
  889.              cp->type = OUTRANGE;
  890.           }
  891.        }
  892.     }
  893. }
  894.  
  895.  
  896. get_3ddata(this_plot)
  897. struct surface_points *this_plot;
  898. {
  899. register int i, j, l_num, xdatum, ydatum;
  900. register FILE *fp;
  901. float x, y, z;
  902. BOOLEAN only_z = FALSE;
  903. char format[MAX_LINE_LEN+1], data_file[MAX_LINE_LEN+1], line[MAX_LINE_LEN+1];
  904. char *float_format = "%f", *float_skip = "%*f";
  905. int xcol = 1, ycol = 2, zcol = 3, pt_in_iso_crv = 0, maxcol;
  906. enum XYZ_order_type {XYZ, YXZ, ZXY, XZY, ZYX, YZX, XY, YX} xyz_order;
  907. struct iso_curve *this_iso;
  908. #ifdef AMIGA_LC_5_1
  909. int num_perc_ast;
  910. char *start_search;
  911. #endif /* AMIGA_LC_5_1 */
  912.         
  913.     quote_str(data_file, c_token);
  914.     this_plot->plot_type = DATA3D;
  915.     this_plot->has_grid_topology = TRUE;
  916.     if ((fp = fopen(data_file, "r")) == (FILE *)NULL)
  917.         os_error("can't open data file", c_token);
  918.  
  919.     format[0] = '\0';
  920.     c_token++;    /* skip data file name */
  921.     if (almost_equals(c_token,"u$sing")) {
  922.         c_token++;      /* skip "using" */
  923.         if (!END_OF_COMMAND && !isstring(c_token)) {
  924.             struct value a;
  925.             zcol = (int)magnitude(const_express(&a));
  926.             only_z = TRUE;
  927.             if (equals(c_token,":")) {
  928.                 c_token++;    /* skip ":" */
  929.                 only_z = FALSE;
  930.                 ycol = zcol;
  931.                 zcol = (int)magnitude(const_express(&a));
  932.                 if (equals(c_token,":")) {
  933.                     c_token++;    /* skip ":" */
  934.                     xcol = ycol;
  935.                     ycol = zcol;
  936.                     zcol = (int)magnitude(const_express(&a));
  937.                 }
  938.                 else {
  939.                     if (mapping3d == MAP3D_CARTESIAN)
  940.                         int_error("Must specify 1 or 3 columns",c_token);
  941.                     xcol = ycol;
  942.                     ycol = zcol;
  943.                 }
  944.             }
  945.             if (!only_z)
  946.                 if ( (xcol == ycol) || (ycol == zcol) || (xcol == zcol))
  947.                     int_error("Columns must be distinct",c_token);
  948.         }
  949.         if (!END_OF_COMMAND && isstring(c_token)) {
  950.             quotel_str(format, c_token);
  951.             c_token++;    /* skip format */
  952.         }
  953.     }
  954.  
  955.     switch (mapping3d) {
  956.         case MAP3D_CARTESIAN:
  957.         maxcol = (xcol > ycol) ? xcol : ycol;
  958.         maxcol = (maxcol > zcol) ? maxcol : zcol;
  959.         if (!only_z) {    /* Determine ordering of input columns */
  960.              if (zcol == maxcol) {
  961.                  if (xcol < ycol)
  962.                      xyz_order = XYZ;  /* scanf(x,y,z) */
  963.                  else
  964.                      xyz_order = YXZ;  /* scanf(y,x,z) */
  965.              }
  966.              else if (ycol == maxcol) {
  967.                  if (xcol < zcol)
  968.                      xyz_order = XZY;  /* scanf(x,z,y) */
  969.                  else
  970.                      xyz_order = ZXY;  /* scanf(z,x,y) */
  971.              }
  972.              else {
  973.                  if (ycol < zcol)
  974.                      xyz_order = YZX;  /* scanf(y,z,x) */
  975.                  else
  976.                      xyz_order = ZYX;  /* scanf(z,y,x) */
  977.              }
  978.         }
  979.         if (strlen(format) == 0) {
  980.             if (only_z) {
  981.                 for(i = 1; i <= zcol; i++)
  982.                     if (i == zcol)
  983.                         (void) strcat(format,float_format);
  984.                     else
  985.                         (void) strcat(format,float_skip);
  986.             }
  987.             else {
  988.                 for(i = 1; i <= maxcol; i++)
  989.                     if ((i == xcol) || (i == ycol) || (i == zcol))
  990.                         (void) strcat(format,float_format);
  991.                     else
  992.                         (void) strcat(format,float_skip);
  993.             }
  994.         }
  995.             break;
  996.         case MAP3D_SPHERICAL:
  997.         case MAP3D_CYLINDRICAL:
  998.         if (only_z)
  999.             int_error("Two columns for spherical/cylindrical coords.",c_token);
  1000.         maxcol = (xcol > ycol) ? xcol : ycol;
  1001.         xyz_order = (xcol < ycol) ? XY : YX;
  1002.         for(i = 1; i <= maxcol; i++)
  1003.             if ((i == xcol) || (i == ycol))
  1004.                 (void) strcat(format,float_format);
  1005.             else
  1006.                 (void) strcat(format,float_skip);
  1007.     }
  1008. #ifdef AMIGA_LC_5_1
  1009.     num_perc_ast = 0;
  1010.     start_search = format;
  1011.     while (*start_search != '\0') {
  1012.         if (start_search[0] == '%')
  1013.             if (start_search[1] == '*') num_perc_ast++;
  1014.         start_search++;
  1015.     }
  1016. #endif /* AMIGA_LC_5_1 */
  1017.  
  1018.     l_num = 0;
  1019.     xdatum = 0;
  1020.     ydatum = 0;
  1021.     this_plot->num_iso_read = 0;
  1022.     this_plot->has_grid_topology = TRUE;
  1023.     if ( this_plot->iso_crvs != NULL ) {
  1024.         struct iso_curve *icrv, *icrvs = this_plot->iso_crvs;
  1025.  
  1026.         while ( icrvs ) {
  1027.         icrv = icrvs;
  1028.         icrvs = icrvs->next;
  1029.         iso_free( icrv );
  1030.         }
  1031.         this_plot->iso_crvs = NULL;
  1032.     }
  1033.     this_iso = iso_alloc( samples );
  1034.  
  1035.     while ( fgets(line, MAX_LINE_LEN, fp) != (char *)NULL ) {
  1036.         l_num++;
  1037.         if (is_comment(line[0]))
  1038.             continue;        /* ignore comments */
  1039.         if (!line[1]) {            /* is it blank line ? */
  1040.                 if (pt_in_iso_crv == 0) {
  1041.                     if (xdatum == 0)
  1042.                     continue;
  1043.                     pt_in_iso_crv = xdatum;
  1044.             }
  1045.  
  1046.             if (xdatum > 0) {
  1047.                 this_iso->p_count = xdatum;
  1048.                 this_iso->next = this_plot->iso_crvs;
  1049.                 this_plot->iso_crvs = this_iso;
  1050.                 this_plot->num_iso_read++;
  1051.  
  1052.                 if (xdatum != pt_in_iso_crv)
  1053.                     this_plot->has_grid_topology = FALSE;
  1054.  
  1055.                 this_iso = iso_alloc(pt_in_iso_crv);
  1056.                 xdatum = 0;
  1057.                 ydatum++;
  1058.             }
  1059.             continue;
  1060.         }
  1061.  
  1062.         if (xdatum >= this_iso->p_max)
  1063.         {
  1064.             /* overflow about to occur. Extend size of points[]
  1065.             * array. We either double the size, or add 1000 points,
  1066.             * whichever is a smaller increment. Note i=p_max.
  1067.             */
  1068.             iso_extend(this_iso,
  1069.                    xdatum + (xdatum < 1000 ? xdatum : 1000));
  1070.         }
  1071.  
  1072. #ifdef AMIGA_LC_5_1
  1073.         switch (sscanf(line, format, &x, &y, &z) - num_perc_ast) {
  1074. #else
  1075.         switch (sscanf(line, format, &x, &y, &z)) {
  1076. #endif
  1077.             case 3:         /* All parameter are specified. */
  1078.                if (!only_z) {
  1079.                 switch (xyz_order) {
  1080.                    case XYZ:    /* scanf(x,y,z) */
  1081.                     this_iso->points[xdatum].x = x;
  1082.                     this_iso->points[xdatum].y = y;
  1083.                     this_iso->points[xdatum].z = z;
  1084.                     break;
  1085.                    case XZY:    /* scanf(x,z,y) */
  1086.                     this_iso->points[xdatum].x = x;
  1087.                     this_iso->points[xdatum].y = z;
  1088.                     this_iso->points[xdatum].z = y;
  1089.                     break;
  1090.                    case YXZ:     /* scanf(y,x,z) */
  1091.                     this_iso->points[xdatum].x = y;
  1092.                     this_iso->points[xdatum].y = x;
  1093.                     this_iso->points[xdatum].z = z;
  1094.                     break;
  1095.                    case ZXY:    /* scanf(z,x,y) */
  1096.                     this_iso->points[xdatum].x = y;
  1097.                     this_iso->points[xdatum].y = z;
  1098.                     this_iso->points[xdatum].z = x;
  1099.                     break;
  1100.                    case YZX:    /* scanf(y,z,x) */
  1101.                     this_iso->points[xdatum].x = z;
  1102.                     this_iso->points[xdatum].y = x;
  1103.                     this_iso->points[xdatum].z = y;
  1104.                     break;
  1105.                    case ZYX:    /* scanf(z,y,x) */
  1106.                     this_iso->points[xdatum].x = z;
  1107.                     this_iso->points[xdatum].y = y;
  1108.                     this_iso->points[xdatum].z = x;
  1109.                     break;
  1110.                 }
  1111.                 if (xyz_order != XYZ) {
  1112.                     x = this_iso->points[xdatum].x;
  1113.                     y = this_iso->points[xdatum].y;
  1114.                     z = this_iso->points[xdatum].z;
  1115.                 }
  1116.                 if (!parametric)
  1117.                     int_error("Must be in parametric mode.",
  1118.                           NO_CARET);
  1119.                    break;
  1120.                }
  1121.             case 1:         /* only one number on the line */
  1122.                if (!only_z)
  1123.                 int_error("3 columns expected, only 1 found", c_token);
  1124.                /* assign that number to z */
  1125.                this_iso->points[xdatum].z = x;
  1126.                z = x;
  1127.                this_iso->points[xdatum].x = xdatum;
  1128.                x = this_iso->points[xdatum].x;
  1129.                this_iso->points[xdatum].y = ydatum;
  1130.                y = this_iso->points[xdatum].y;
  1131.                if (parametric)
  1132.                 int_error("Must be in non parametric mode.",
  1133.                       NO_CARET);
  1134.                break;
  1135.             case 2:
  1136.                switch (xyz_order) {
  1137.                    case YX:
  1138.                    z = x;    /* Use z as temp */
  1139.                    x = y;
  1140.                    y = z;
  1141.                    break;
  1142.                    default:
  1143.                    break;
  1144.                }
  1145.                switch (mapping3d) {
  1146.                    case MAP3D_CARTESIAN:
  1147.                        int_error("2 columns found, 3 expected",
  1148.                          c_token);
  1149.                    break;
  1150.                    case MAP3D_SPHERICAL:
  1151.                    if (angles_format == ANGLES_DEGREES) {
  1152.                        x *= DEG2RAD; /* Convert to radians. */
  1153.                        y *= DEG2RAD;
  1154.                    }
  1155.                    this_iso->points[xdatum].x = cos(x) * cos(y);
  1156.                    this_iso->points[xdatum].y = sin(x) * cos(y);
  1157.                    this_iso->points[xdatum].z = sin(y);
  1158.                    break;
  1159.                    case MAP3D_CYLINDRICAL:
  1160.                    if (angles_format == ANGLES_DEGREES)
  1161.                        x *= DEG2RAD; /* Convert to radians. */
  1162.                    this_iso->points[xdatum].x = cos(x);
  1163.                    this_iso->points[xdatum].y = sin(x);
  1164.                    this_iso->points[xdatum].z = y;
  1165.                    break;
  1166.                }
  1167.                x = this_iso->points[xdatum].x;
  1168.                y = this_iso->points[xdatum].y;
  1169.                z = this_iso->points[xdatum].z;
  1170.                break;
  1171.             default:
  1172.                (void) sprintf(line, "bad data on line %d", l_num);
  1173.                int_error(line,c_token);
  1174.         }
  1175.  
  1176.         if (log_x) {
  1177.             if (x < 0.0)
  1178.             int_error("X value must be above 0 for log scale!",
  1179.                   NO_CARET);
  1180.             else
  1181.             this_iso->points[xdatum].x =
  1182.                 log10(this_iso->points[xdatum].x);
  1183.         }
  1184.         if (log_y) {
  1185.             if (y < 0.0)
  1186.             int_error("Y value must be above 0 for log scale!",
  1187.                   NO_CARET);
  1188.             else
  1189.             this_iso->points[xdatum].y =
  1190.                 log10(this_iso->points[xdatum].y);
  1191.         }
  1192.         if (log_z) {
  1193.             if (z < 0.0)
  1194.             int_error("Z value must be above 0 for log scale!",
  1195.                   NO_CARET);
  1196.             else
  1197.             this_iso->points[xdatum].z =
  1198.                 log10(this_iso->points[xdatum].z);
  1199.         }
  1200.  
  1201.         if (autoscale_lx) {
  1202.             if (x < xmin) xmin = x;
  1203.             if (x > xmax) xmax = x;
  1204.         }
  1205.         if (autoscale_ly) {
  1206.             if (y < ymin) ymin = y;
  1207.             if (y > ymax) ymax = y;
  1208.         }
  1209.         if (autoscale_lz) {
  1210.             if (z < zmin) zmin = z;
  1211.             if (z > zmax) zmax = z;
  1212.         }
  1213.  
  1214.         xdatum++;
  1215.     }
  1216.  
  1217.     if (xdatum > 0) {
  1218.         this_plot->num_iso_read++; /* Update last iso. */
  1219.         this_iso->p_count = xdatum;
  1220.  
  1221.         this_iso->next = this_plot->iso_crvs; 
  1222.         this_plot->iso_crvs = this_iso;
  1223.  
  1224.         if (xdatum != pt_in_iso_crv)
  1225.             this_plot->has_grid_topology = FALSE;
  1226.  
  1227.     }
  1228.     else {
  1229.         iso_free(this_iso);/* Free last allocation. */
  1230.     }
  1231.  
  1232.     (void) fclose(fp);
  1233.  
  1234.     if (this_plot->has_grid_topology) {
  1235.             struct iso_curve *new_icrvs = NULL;
  1236.         int num_new_iso = this_plot->iso_crvs->p_count,
  1237.             len_new_iso = this_plot->num_iso_read;
  1238.  
  1239.         /* Now we need to set the other direction (pseudo) isolines. */
  1240.             for (i = 0; i < num_new_iso; i++) {
  1241.             struct iso_curve *new_icrv = iso_alloc(len_new_iso);
  1242.  
  1243.             new_icrv->p_count = len_new_iso;
  1244.  
  1245.             for (j = 0, this_iso = this_plot->iso_crvs;
  1246.                  this_iso != NULL;
  1247.              j++, this_iso = this_iso->next) {
  1248.             new_icrv->points[j].x = this_iso->points[i].x;
  1249.                  new_icrv->points[j].y = this_iso->points[i].y;
  1250.                  new_icrv->points[j].z = this_iso->points[i].z;
  1251.             }
  1252.  
  1253.             new_icrv->next = new_icrvs;
  1254.             new_icrvs = new_icrv;
  1255.         }
  1256.  
  1257.         /* Append the new iso curves after the read ones. */
  1258.         for (this_iso = this_plot->iso_crvs;
  1259.              this_iso->next != NULL;
  1260.              this_iso = this_iso->next);
  1261.         this_iso->next = new_icrvs;
  1262.     }
  1263. }
  1264.  
  1265. /* print_points:
  1266.  * a debugging routine to print out the points of a curve,
  1267.  * and the curve structure. If curve<0, then we print the 
  1268.  * list of curves.
  1269.  */
  1270. static char *plot_type_names[4] = {
  1271.     "Function", "Data", "3D Function", "3d data"
  1272. };
  1273. static char *plot_style_names[6] = {
  1274.     "Lines", "Points", "Impulses", "LinesPoints", "Dots", "Errorbars"
  1275. };
  1276.  
  1277. print_points(curve)
  1278.     int curve;            /* which curve to print */
  1279. {
  1280.     register struct curve_points *this_plot;
  1281.     int i;
  1282.  
  1283.     if (curve < 0) {
  1284.        for (this_plot = first_plot, i=0; 
  1285.            this_plot != NULL; 
  1286.            i++, this_plot = this_plot->next_cp) {
  1287.           printf("Curve %d:\n", i);
  1288.           if ((int)this_plot->plot_type >= 0 && (int)(this_plot->plot_type) < 4)
  1289.             printf("Plot type %d: %s\n", (int)(this_plot->plot_type),
  1290.                  plot_type_names[(int)(this_plot->plot_type)]);
  1291.           else
  1292.             printf("Plot type %d: BAD\n", (int)(this_plot->plot_type));
  1293.           if ((int)this_plot->plot_style >= 0 && (int)(this_plot->plot_style) < 6)
  1294.             printf("Plot style %d: %s\n", (int)(this_plot->plot_style),
  1295.                  plot_style_names[(int)(this_plot->plot_style)]);
  1296.           else
  1297.             printf("Plot style %d: BAD\n", (int)(this_plot->plot_style));
  1298.           printf("Plot title: '%s'\n", this_plot->title);
  1299.           printf("Line type %d\n", this_plot->line_type);
  1300.           printf("Point type %d\n", this_plot->point_type);
  1301.           printf("max points %d\n", this_plot->p_max);
  1302.           printf("current points %d\n", this_plot->p_count);
  1303.           printf("\n");
  1304.        }
  1305.     } else {
  1306.        for (this_plot = first_plot, i = 0; 
  1307.            i < curve && this_plot != NULL; 
  1308.            i++, this_plot = this_plot->next_cp)
  1309.         ;
  1310.        if (this_plot == NULL)
  1311.         printf("Curve %d does not exist; list has %d curves\n", curve, i);
  1312.        else {
  1313.           printf ("Curve %d, %d points\n", curve, this_plot->p_count);
  1314.           for (i = 0; i < this_plot->p_count; i++) {
  1315.              printf("%c x=%g y=%g z=%g ylow=%g yhigh=%g\n", 
  1316.                    this_plot->points[i].type == INRANGE ? 'i'
  1317.                    : this_plot->points[i].type == OUTRANGE ? 'o'
  1318.                    : 'u',
  1319.                    this_plot->points[i].x,
  1320.                    this_plot->points[i].y,
  1321.                    this_plot->points[i].z,
  1322.                    this_plot->points[i].ylow,
  1323.                    this_plot->points[i].yhigh);
  1324.           }
  1325.           printf("\n");
  1326.        }
  1327.     }    
  1328. }
  1329.  
  1330.  
  1331. /* This parses the plot command after any range specifications. 
  1332.  * To support autoscaling on the x axis, we want any data files to 
  1333.  * define the x range, then to plot any functions using that range. 
  1334.  * We thus parse the input twice, once to pick up the data files, 
  1335.  * and again to pick up the functions. Definitions are processed 
  1336.  * twice, but that won't hurt.
  1337.  */
  1338. eval_plots()
  1339. {
  1340. register int i;
  1341. register struct curve_points *this_plot, **tp_ptr;
  1342. register int start_token, end_token;
  1343. register int begin_token;
  1344. double x_min, x_max, y_min, y_max;
  1345. register double x, xdiff, temp;
  1346. static struct value a;
  1347. BOOLEAN ltmp, some_data_files = FALSE;
  1348. int plot_num, line_num, point_num, xparam=0;
  1349. char *xtitle;
  1350. void parametric_fixup();
  1351.  
  1352.     if (autoscale_ly) {
  1353.         ymin = VERYLARGE;
  1354.         ymax = -VERYLARGE;
  1355.     } else if (log_y && (ymin <= 0.0 || ymax <= 0.0))
  1356.             int_error("y range must be above 0 for log scale!",
  1357.                 NO_CARET);
  1358.  
  1359.     tp_ptr = &(first_plot);
  1360.     plot_num = 0;
  1361.     line_num = 0;     /* default line type */
  1362.     point_num = 0;    /* default point type */
  1363.  
  1364.     xtitle = NULL;
  1365.  
  1366.     begin_token = c_token;
  1367.  
  1368. /*** First Pass: Read through data files ***/
  1369. /* This pass serves to set the xrange and to parse the command, as well 
  1370.  * as filling in every thing except the function data. That is done after
  1371.  * the xrange is defined.
  1372.  */
  1373.     while (TRUE) {
  1374.         if (END_OF_COMMAND)
  1375.             int_error("function to plot expected",c_token);
  1376.  
  1377.         start_token = c_token;
  1378.  
  1379.         if (is_definition(c_token)) {
  1380.             define();
  1381.         } else {
  1382.             plot_num++;
  1383.  
  1384.             if (isstring(c_token)) {            /* data file to plot */
  1385.                 if (parametric && xparam) 
  1386.                     int_error("previous parametric function not fully specified",
  1387.                                                                     c_token);
  1388.  
  1389.                 if (!some_data_files && autoscale_lx) {
  1390.                     xmin = VERYLARGE;
  1391.                     xmax = -VERYLARGE;
  1392.                 }
  1393.                 some_data_files = TRUE;
  1394.  
  1395.                 if (*tp_ptr)
  1396.                   this_plot = *tp_ptr;
  1397.                 else {        /* no memory malloc()'d there yet */
  1398.                     this_plot = cp_alloc(MIN_CRV_POINTS);
  1399.                     *tp_ptr = this_plot;
  1400.                 }
  1401.                 this_plot->plot_type = DATA;
  1402.                 this_plot->plot_style = data_style;
  1403.                 end_token = c_token;
  1404.                 get_data(this_plot); /* this also parses the using option */
  1405.             } 
  1406.             else {                            /* function to plot */
  1407.                 if (parametric)            /* working on x parametric function */
  1408.                     xparam = 1 - xparam;
  1409.                 if (*tp_ptr) {
  1410.                     this_plot = *tp_ptr;
  1411.                     cp_extend(this_plot, samples+1);
  1412.                 } else {        /* no memory malloc()'d there yet */
  1413.                     this_plot = cp_alloc(samples+1);
  1414.                     *tp_ptr = this_plot;
  1415.                 }
  1416.                 this_plot->plot_type = FUNC;
  1417.                 this_plot->plot_style = func_style;
  1418.                 dummy_func = &plot_func;
  1419.                 plot_func.at = temp_at();
  1420.                 /* ignore it for now */
  1421.                 end_token = c_token-1;
  1422.             }
  1423.  
  1424.             if (almost_equals(c_token,"t$itle")) {
  1425.                 if (parametric) {
  1426.                     if (xparam) 
  1427.                         int_error(
  1428.         "\"title\" allowed only after parametric function fully specified",
  1429.                                                                     c_token);
  1430.                     else if (xtitle != NULL)
  1431.                         xtitle[0] = '\0';  /* Remove default title .*/
  1432.                 }
  1433.                 c_token++;
  1434.                 if ( isstring( c_token ) ) {
  1435.                     m_quote_capture(&(this_plot->title),c_token,c_token);
  1436.                 }
  1437.                 else {
  1438.                     int_error("expecting \"title\" for plot",c_token);
  1439.                 }
  1440.                 c_token++;
  1441.             }
  1442.               else {
  1443.                   m_capture(&(this_plot->title),start_token,end_token);
  1444.                  if (xparam) xtitle = this_plot->title;
  1445.               }
  1446.   
  1447.               this_plot->line_type = line_num;
  1448.             this_plot->point_type = point_num;
  1449.  
  1450.             if (almost_equals(c_token,"w$ith")) {
  1451.                 if (parametric && xparam) 
  1452.                     int_error("\"with\" allowed only after parametric function fully specified",
  1453.                                     c_token);
  1454.                 this_plot->plot_style = get_style();
  1455.             }
  1456.  
  1457.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  1458.                 struct value t;
  1459.                 this_plot->line_type = (int)real(const_express(&t))-1;
  1460.             }
  1461.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  1462.                 struct value t;
  1463.                 this_plot->point_type = (int)real(const_express(&t))-1;
  1464.             }
  1465.             if ( (this_plot->plot_style == POINTS) ||
  1466.                  (this_plot->plot_style == LINESPOINTS) ||
  1467.                  (this_plot->plot_style == ERRORBARS) )
  1468.                     if (!xparam) point_num++;
  1469.             if (!xparam) line_num++;
  1470.  
  1471.             if (this_plot->plot_type == DATA) 
  1472.               /* now that we know the plot style, adjust the yrange */
  1473.               adjust_yrange(this_plot);
  1474.  
  1475.             tp_ptr = &(this_plot->next_cp);
  1476.         }
  1477.  
  1478.         if (equals(c_token,",")) 
  1479.             c_token++;
  1480.         else  
  1481.             break;
  1482.     }
  1483.  
  1484.     if (parametric && xparam) 
  1485.         int_error("parametric function not fully specified", NO_CARET);
  1486.  
  1487.     if (parametric) {
  1488.     /* Swap t and x ranges for duration of these eval_plot computations. */
  1489.         ltmp = autoscale_lx; autoscale_lx = autoscale_lt; autoscale_lt = ltmp;
  1490.         temp = xmin; xmin = tmin; tmin = temp;
  1491.         temp = xmax; xmax = tmax; tmax = temp;
  1492.     }
  1493.  
  1494. /*** Second Pass: Evaluate the functions ***/
  1495. /* Everything is defined now, except the function data. We expect
  1496.  * no syntax errors, etc, since the above parsed it all. This makes 
  1497.  * the code below simpler. If autoscale_ly, the yrange may still change.
  1498.  */
  1499.      if (fabs(xmax-xmin) < zero)
  1500.       if (autoscale_lx) {
  1501.          fprintf(stderr, "Warning: empty %c range [%g:%g], ", 
  1502.             parametric ? 't' : 'x', xmin,xmax);
  1503.          if (fabs(xmin) < zero) {
  1504.             /* completely arbitary */
  1505.             xmin = -1.;
  1506.             xmax = 1.;
  1507.          } else {
  1508.             /* expand range by 10% in either direction */
  1509.             xmin = xmin * 0.9;
  1510.             xmax = xmax * 1.1;
  1511.          }
  1512.          fprintf(stderr, "adjusting to [%g:%g]\n", xmin,xmax);
  1513.       } else {
  1514.          int_error("x range is less than `zero`", c_token);
  1515.       }
  1516.  
  1517.     /* give error if xrange badly set from missing datafile error */
  1518.     if (xmin == VERYLARGE || xmax == -VERYLARGE) {
  1519.         int_error("x range is invalid", c_token);
  1520.     }
  1521.  
  1522.     if (log_x) {
  1523.        if (xmin <= 0.0 || xmax <= 0.0)
  1524.         int_error("x range must be greater than 0 for log scale!",NO_CARET);
  1525.        x_min = log10(xmin);
  1526.        x_max = log10(xmax);
  1527.     } else {
  1528.        x_min = xmin;
  1529.        x_max = xmax;
  1530.     }
  1531.  
  1532.     xdiff = (x_max - x_min) / (samples - 1);
  1533.  
  1534.     tp_ptr = &(first_plot);
  1535.     plot_num = 0;
  1536.     this_plot = first_plot;
  1537.     c_token = begin_token;    /* start over */
  1538.  
  1539.     /* Read through functions */
  1540.     while (TRUE) {
  1541.         if (is_definition(c_token)) {
  1542.             define();
  1543.         } else {
  1544.             plot_num++;
  1545.             if (isstring(c_token)) {            /* data file to plot */
  1546.                 /* ignore this now */
  1547.                 c_token++;
  1548.                 if (almost_equals(c_token,"u$sing")) {
  1549.                     c_token++;      /* skip "using" */
  1550.                         if (!isstring(c_token)) {
  1551.                         struct value a;
  1552.                         (void)magnitude(const_express(&a)); /* skip xcol */
  1553.                         if (equals(c_token,":")) {
  1554.                             c_token++;      /* skip ":" */
  1555.                             (void)magnitude(const_express(&a)); /* skip ycol */
  1556.                         }
  1557.                         if (equals(c_token,":")) {
  1558.                             c_token++;      /* skip ":" */
  1559.                             (void)magnitude(const_express(&a)); /* skip yemin */
  1560.                         }
  1561.                         if (equals(c_token,":")) {
  1562.                             c_token++;      /* skip ":" */
  1563.                             (void)magnitude(const_express(&a)); /* skip yemax */
  1564.                         }
  1565.                     }
  1566.                     if (isstring(c_token))
  1567.                         c_token++;      /* skip format string */
  1568.                 }
  1569.             }
  1570.             else {                    /* function to plot */
  1571.                 if (parametric)            /* working on x parametric function */
  1572.                     xparam = 1 - xparam;
  1573.                 dummy_func = &plot_func;
  1574.                 plot_func.at = temp_at(); /* reparse function */
  1575.  
  1576.                 for (i = 0; i < samples; i++) {
  1577.                     x = x_min + i*xdiff;
  1578.                     /* if (log_x) PEM fix logscale x axis */
  1579.                     /* x = pow(10.0,x); 26-Sep-89 */
  1580.                     (void) complex(&plot_func.dummy_values[0],
  1581.                                 log_x ? pow(10.0,x) : x,
  1582.                                 0.0);
  1583.  
  1584.                     evaluate_at(plot_func.at,&a);
  1585.  
  1586.                     if (undefined || (fabs(imag(&a)) > zero)) {
  1587.                        this_plot->points[i].type = UNDEFINED;
  1588.                        continue;
  1589.                     }
  1590.  
  1591.                     temp = real(&a);
  1592.  
  1593.                     if (log_y && temp < 0.0) {
  1594.                        this_plot->points[i].type = UNDEFINED;
  1595.                        continue;
  1596.                     }
  1597.  
  1598.                     this_plot->points[i].x = x;
  1599.                     if (log_y) {
  1600.                        if (temp == 0.0) {
  1601.                           this_plot->points[i].type = OUTRANGE;
  1602.                           this_plot->points[i].y = -VERYLARGE;
  1603.                           continue;
  1604.                        } else {
  1605.                           this_plot->points[i].y = log10(temp);
  1606.                        }
  1607.                     } else
  1608.                      this_plot->points[i].y = temp;
  1609.  
  1610.                     if (autoscale_ly || polar
  1611.                        || inrange(temp, ymin, ymax)) {
  1612.                        this_plot->points[i].type = INRANGE;
  1613.                     /* When xparam is 1 we are not really computing y's! */
  1614.                         if (!xparam && autoscale_ly) {
  1615.                            if (temp < ymin) ymin = temp;
  1616.                            if (temp > ymax) ymax = temp;
  1617.                         }
  1618.                     } else
  1619.                      this_plot->points[i].type = OUTRANGE;
  1620.                 }
  1621.                 this_plot->p_count = i; /* samples */
  1622.              }
  1623.  
  1624.             /* title was handled above */
  1625.             if (almost_equals(c_token,"t$itle")) {
  1626.                 c_token++;
  1627.                 c_token++;
  1628.             }
  1629.  
  1630.             /* style was handled above */
  1631.             if (almost_equals(c_token,"w$ith")) {
  1632.                 c_token++;
  1633.                 c_token++;
  1634.             }
  1635.  
  1636.             /* line and point types were handled above */
  1637.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  1638.                 struct value t;
  1639.                 (void)real(const_express(&t));
  1640.             }
  1641.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  1642.                 struct value t;
  1643.                 (void)real(const_express(&t));
  1644.             }
  1645.  
  1646.              tp_ptr = &(this_plot->next_cp); /* used below */
  1647.             this_plot = this_plot->next_cp;
  1648.          }
  1649.         
  1650.         if (equals(c_token,",")) 
  1651.           c_token++;
  1652.         else  
  1653.           break;
  1654.      }
  1655.  
  1656.     /* throw out all curve_points at end of list, that we don't need  */
  1657.     cp_free(*tp_ptr);
  1658.     *tp_ptr = NULL;
  1659.  
  1660.     if (fabs(ymax - ymin) < zero)
  1661.      /* if autoscale, widen range */
  1662.      if (autoscale_ly) {
  1663.         fprintf(stderr, "Warning: empty y range [%g:%g], ", ymin, ymax);
  1664.         if (fabs(ymin) < zero) {
  1665.             ymin = -1.;
  1666.             ymax = 1.;
  1667.         } else {
  1668.             /* expand range by 10% in either direction */
  1669.             ymin = ymin * 0.9;
  1670.             ymax = ymax * 1.1;
  1671.         }
  1672.         fprintf(stderr, "adjusting to [%g:%g]\n", ymin, ymax);
  1673.      } else {
  1674.         int_error("y range is less than `zero`", c_token);
  1675.      }
  1676.  
  1677. /* Now we finally know the real ymin and ymax */
  1678.     if (log_y) {
  1679.         y_min = log10(ymin);
  1680.         y_max = log10(ymax);
  1681.     } else {
  1682.         y_min = ymin;
  1683.         y_max = ymax;
  1684.     }
  1685.     capture(replot_line,plot_token,c_token);
  1686.  
  1687.     if (parametric) {
  1688.     /* Now put t and x ranges back before we actually plot anything. */
  1689.         ltmp = autoscale_lx; autoscale_lx = autoscale_lt; autoscale_lt = ltmp;
  1690.         temp = xmin; xmin = tmin; tmin = temp;
  1691.         temp = xmax; xmax = tmax; tmax = temp;
  1692.         if (some_data_files && autoscale_lx) {
  1693.         /* 
  1694.             Stop any further autoscaling in this case (may be a mistake, have
  1695.               to consider what is really wanted some day in the future--jdc). 
  1696.         */
  1697.             autoscale_lx = 0;
  1698.         }
  1699.     /* Now actually fix the plot pairs to be single plots. */
  1700.         parametric_fixup (first_plot, &plot_num, &x_min, &x_max);
  1701.     }
  1702.  
  1703.     do_plot(first_plot,plot_num,x_min,x_max,y_min,y_max);
  1704.     cp_free(first_plot);
  1705.     first_plot = NULL;
  1706. }
  1707.  
  1708. /* This parses the splot command after any range specifications. 
  1709.  * To support autoscaling on the x/z axis, we want any data files to 
  1710.  * define the x/y range, then to plot any functions using that range. 
  1711.  * We thus parse the input twice, once to pick up the data files, 
  1712.  * and again to pick up the functions. Definitions are processed 
  1713.  * twice, but that won't hurt.
  1714.  */
  1715. eval_3dplots()
  1716. {
  1717. register int i,j,k;
  1718. register struct surface_points *this_plot, **tp_3d_ptr;
  1719. register int start_token, end_token;
  1720. register int begin_token;
  1721. double x_min, x_max, y_min, y_max, z_min, z_max;
  1722. register double x, xdiff, xisodiff, y, ydiff, yisodiff, temp;
  1723. static struct value a;
  1724. BOOLEAN ltmp, some_data_files = FALSE;
  1725. int plot_num, line_num, point_num,
  1726.     crnt_param = 0; /* 0=x, 1=y, 2=z */
  1727. char *xtitle;
  1728. char *ytitle;
  1729. void parametric_3dfixup();
  1730.  
  1731.     if (autoscale_lz) {
  1732.         zmin = VERYLARGE;
  1733.         zmax = -VERYLARGE;
  1734.     } else if (log_z && (zmin <= 0.0 || zmax <= 0.0))
  1735.             int_error("z range must be above 0 for log scale!",
  1736.                 NO_CARET);
  1737.  
  1738.     tp_3d_ptr = &(first_3dplot);
  1739.     plot_num = 0;
  1740.     line_num = 0;     /* default line type */
  1741.     point_num = 0;    /* default point type */
  1742.  
  1743.     xtitle = NULL;
  1744.     ytitle = NULL;
  1745.  
  1746.     begin_token = c_token;
  1747.  
  1748. /*** First Pass: Read through data files ***/
  1749. /* This pass serves to set the x/yranges and to parse the command, as well 
  1750.  * as filling in every thing except the function data. That is done after
  1751.  * the x/yrange is defined.
  1752.  */
  1753.     while (TRUE) {
  1754.         if (END_OF_COMMAND)
  1755.             int_error("function to plt3d expected",c_token);
  1756.  
  1757.         start_token = c_token;
  1758.  
  1759.         if (is_definition(c_token)) {
  1760.             define();
  1761.         } else {
  1762.             plot_num++;
  1763.  
  1764.             if (isstring(c_token)) {            /* data file to plot */
  1765.                 if (parametric && crnt_param != 0)
  1766.                     int_error("previous parametric function not fully specified",
  1767.                                                                     c_token);
  1768.  
  1769.                 if (!some_data_files) {
  1770.                     if (autoscale_lx) {
  1771.                         xmin = VERYLARGE;
  1772.                         xmax = -VERYLARGE;
  1773.                     }
  1774.                     if (autoscale_ly) {
  1775.                         ymin = VERYLARGE;
  1776.                         ymax = -VERYLARGE;
  1777.                     }
  1778.                 }
  1779.  
  1780.                 some_data_files = TRUE;
  1781.  
  1782.                 if (*tp_3d_ptr)
  1783.                     this_plot = *tp_3d_ptr;
  1784.                 else {    /* no memory malloc()'d there yet */
  1785.                     /* Allocate samples * iso_samples twice for */
  1786.                     /* Each of the isoparametric direction. */
  1787.                     this_plot = sp_alloc(0,0);
  1788.                     *tp_3d_ptr = this_plot;
  1789.                 }
  1790.  
  1791.                 this_plot->plot_type = DATA3D;
  1792.                 this_plot->plot_style = data_style;
  1793.                 end_token = c_token;
  1794.                 get_3ddata(this_plot); /* this also parses the using option */
  1795.             } 
  1796.             else {                        /* function to plot */
  1797.                 if (parametric) /* Rotate between x/y/z axes */
  1798.                     crnt_param = (crnt_param+1) % 3;
  1799.                 if (*tp_3d_ptr) {
  1800.                     this_plot = *tp_3d_ptr;
  1801.                     sp_replace(this_plot,samples,2*iso_samples);
  1802.                 }
  1803.                 else {    /* no memory malloc()'d there yet */
  1804.                     /* Allocate samples * iso_samples twice for */
  1805.                     /* Each of the isoparametric direction. */
  1806.                     this_plot = sp_alloc(samples,2*iso_samples);
  1807.                     *tp_3d_ptr = this_plot;
  1808.                 }
  1809.  
  1810.                 this_plot->plot_type = FUNC3D;
  1811.                 this_plot->has_grid_topology = TRUE;
  1812.                 this_plot->plot_style = func_style;
  1813.                 dummy_func = &plot_func;
  1814.                 plot_func.at = temp_at();
  1815.                 /* ignore it for now */
  1816.                 end_token = c_token-1;
  1817.             }
  1818.  
  1819.             if (almost_equals(c_token,"t$itle")) {
  1820.                 if (parametric) {
  1821.                         if (crnt_param)
  1822.                         int_error(
  1823.         "\"title\" allowed only after parametric function fully specified",
  1824.                                                                     c_token);
  1825.                     else {
  1826.                         /* Remove default title */
  1827.                         if (xtitle != NULL)
  1828.                             xtitle[0] = '\0';
  1829.                         if (ytitle != NULL)
  1830.                             ytitle[0] = '\0';
  1831.                     }
  1832.                     }
  1833.                 c_token++;
  1834.                 if ( isstring( c_token ) ) {
  1835.                     m_quote_capture(&(this_plot->title),c_token,c_token);
  1836.                 }
  1837.                 else {
  1838.                     int_error("expecting \"title\" for plot",c_token);
  1839.                 }
  1840.                 c_token++;
  1841.             }
  1842.               else {
  1843.                   m_capture(&(this_plot->title),start_token,end_token);
  1844.                 if (crnt_param == 1) xtitle = this_plot->title;
  1845.                 if (crnt_param == 2) ytitle = this_plot->title;
  1846.               }
  1847.   
  1848.               this_plot->line_type = line_num;
  1849.             this_plot->point_type = point_num;
  1850.  
  1851.             if (almost_equals(c_token,"w$ith")) {
  1852.                 this_plot->plot_style = get_style();
  1853.             }
  1854.  
  1855.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  1856.                 struct value t;
  1857.                 this_plot->line_type = (int)real(const_express(&t))-1;
  1858.             }
  1859.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  1860.                 struct value t;
  1861.                 this_plot->point_type = (int)real(const_express(&t))-1;
  1862.             }
  1863.             if ( (this_plot->plot_style == POINTS) ||
  1864.                  (this_plot->plot_style == LINESPOINTS) ||
  1865.                  (this_plot->plot_style == ERRORBARS) )
  1866.                     if (crnt_param == 0)
  1867.                         point_num +=
  1868.                             1 + (draw_contour != 0);
  1869.             if (crnt_param == 0)
  1870.                 line_num += 1 + (draw_contour != 0);
  1871.  
  1872.             tp_3d_ptr = &(this_plot->next_sp);
  1873.         }
  1874.  
  1875.         if (equals(c_token,",")) 
  1876.             c_token++;
  1877.         else  
  1878.             break;
  1879.     }
  1880.  
  1881.     if (parametric && crnt_param != 0)
  1882.         int_error("parametric function not fully specified", NO_CARET);
  1883.  
  1884.     if (parametric) {
  1885.     /* Swap u/v and x/y ranges for duration of these eval_plot computations. */
  1886.         ltmp = autoscale_lx; autoscale_lx = autoscale_lu; autoscale_lu = ltmp;
  1887.         ltmp = autoscale_ly; autoscale_ly = autoscale_lv; autoscale_lv = ltmp;
  1888.         temp = xmin; xmin = umin; umin = temp;
  1889.         temp = xmax; xmax = umax; umax = temp;
  1890.         temp = ymin; ymin = vmin; vmin = temp;
  1891.         temp = ymax; ymax = vmax; vmax = temp;
  1892.     }
  1893. /*** Second Pass: Evaluate the functions ***/
  1894. /* Everything is defined now, except the function data. We expect
  1895.  * no syntax errors, etc, since the above parsed it all. This makes 
  1896.  * the code below simpler. If autoscale_ly, the yrange may still change.
  1897.  */
  1898.      if (xmin == xmax)
  1899.       if (autoscale_lx) {
  1900.          fprintf(stderr, "Warning: empty x range [%g:%g], ", 
  1901.              xmin,xmax);
  1902.          if (xmin == 0.0) {
  1903.             /* completely arbitary */
  1904.             xmin = -1.;
  1905.             xmax = 1.;
  1906.          } else {
  1907.             /* expand range by 10% in either direction */
  1908.             xmin = xmin * 0.9;
  1909.             xmax = xmax * 1.1;
  1910.          }
  1911.          fprintf(stderr, "adjusting to [%g:%g]\n", xmin,xmax);
  1912.       } else {
  1913.          int_error("x range is empty", c_token);
  1914.       }
  1915.  
  1916.      if (ymin == ymax)
  1917.       if (autoscale_ly) {
  1918.          fprintf(stderr, "Warning: empty y range [%g:%g], ", 
  1919.             ymin,ymax);
  1920.          if (ymin == 0.0) {
  1921.             /* completely arbitary */
  1922.             ymin = -1.;
  1923.             ymax = 1.;
  1924.          } else {
  1925.             /* expand range by 10% in either direction */
  1926.             ymin = ymin * 0.9;
  1927.             ymax = ymax * 1.1;
  1928.          }
  1929.          fprintf(stderr, "adjusting to [%g:%g]\n", ymin,ymax);
  1930.       } else {
  1931.          int_error("y range is empty", c_token);
  1932.       }
  1933.  
  1934.     /* give error if xrange badly set from missing datafile error */
  1935.     if (xmin == VERYLARGE || xmax == -VERYLARGE) {
  1936.     int_error("x range is invalid", c_token);
  1937.     }
  1938.  
  1939.     if (log_x) {
  1940.        if (xmin <= 0.0 || xmax <= 0.0)
  1941.         int_error("x range must be greater than 0 for log scale!",NO_CARET);
  1942.        x_min = log10(xmin);
  1943.        x_max = log10(xmax);
  1944.     } else {
  1945.        x_min = xmin;
  1946.        x_max = xmax;
  1947.     }
  1948.  
  1949.     if (log_y) {
  1950.        if (ymin <= 0.0 || ymax <= 0.0)
  1951.         int_error("y range must be greater than 0 for log scale!",NO_CARET);
  1952.        y_min = log10(ymin);
  1953.        y_max = log10(ymax);
  1954.     } else {
  1955.        y_min = ymin;
  1956.        y_max = ymax;
  1957.     }
  1958.  
  1959.     if (samples < 2 || iso_samples < 2)
  1960.     int_error("samples or iso_samples < 2. Must be at least 2.\n");
  1961.  
  1962.     xdiff = (x_max - x_min) / (samples - 1);
  1963.     ydiff = (y_max - y_min) / (samples - 1);
  1964.     xisodiff = (x_max - x_min) / (iso_samples - 1);
  1965.     yisodiff = (y_max - y_min) / (iso_samples - 1);
  1966.  
  1967.     plot_num = 0;
  1968.     this_plot = first_3dplot;
  1969.     c_token = begin_token;    /* start over */
  1970.  
  1971.     /* Read through functions */
  1972.     while (TRUE) {
  1973.         if (is_definition(c_token)) {
  1974.             define();
  1975.         } else {
  1976.             plot_num++;
  1977.             if (isstring(c_token)) {            /* data file to plot */
  1978.                 /* ignore this now */
  1979.                 c_token++;
  1980.                 if (almost_equals(c_token,"u$sing")) {
  1981.                     c_token++;      /* skip "using" */
  1982.                                 if (!isstring(c_token)) {
  1983.                         struct value a;
  1984.                         (void)magnitude(const_express(&a));    /* skip xcol */
  1985.                         if (equals(c_token,":")) {
  1986.                             c_token++;    /* skip ":" */
  1987.                             (void)magnitude(const_express(&a));    /* skip ycol */
  1988.                             if (equals(c_token,":")) {
  1989.                                 c_token++;    /* skip ":" */
  1990.                                 (void)magnitude(const_express(&a));    /* skip zcol */
  1991.                             }
  1992.                         }
  1993.                     }
  1994.                                 if (isstring(c_token))
  1995.                         c_token++;    /* skip format string */
  1996.                 }
  1997.             }
  1998.             else {                    /* function to plot */
  1999.                 struct iso_curve *this_iso = this_plot->iso_crvs;
  2000.                 struct coordinate *points = this_iso->points;
  2001.                 
  2002.                 if (parametric)
  2003.                     crnt_param = (crnt_param+1) % 3;
  2004.                 dummy_func = &plot_func;
  2005.                 plot_func.at = temp_at(); /* reparse function */
  2006.  
  2007.                 for (j = 0; j < iso_samples; j++) {
  2008.                     y = y_min + j*yisodiff;
  2009.                     /* if (log_y) PEM fix logscale y axis */
  2010.                     /* y = pow(10.0,y); 26-Sep-89 */
  2011.                     (void) complex(&plot_func.dummy_values[1],
  2012.                                 log_y ? pow(10.0,y) : y,
  2013.                                 0.0);
  2014.  
  2015.                     for (i = 0; i < samples; i++) {
  2016.                         x = x_min + i*xdiff;
  2017.                         /* if (log_x) PEM fix logscale x axis */
  2018.                         /* x = pow(10.0,x); 26-Sep-89 */
  2019.                         (void) complex(&plot_func.dummy_values[0],
  2020.                                 log_x ? pow(10.0,x) : x,
  2021.                                 0.0);
  2022.  
  2023.                         points[i].x = x;
  2024.                         points[i].y = y;
  2025.  
  2026.                         evaluate_at(plot_func.at,&a);
  2027.  
  2028.                         if (undefined || (fabs(imag(&a)) > zero)) {
  2029.                        points[i].type = UNDEFINED;
  2030.                        continue;
  2031.                         }
  2032.  
  2033.                         temp = real(&a);
  2034.  
  2035.                         if (log_z && temp < 0.0) {
  2036.                        points[i].type = UNDEFINED;
  2037.                        continue;
  2038.                         }
  2039.  
  2040.                         if (log_z) {
  2041.                        if (temp == 0.0) {
  2042.                           points[i].type = OUTRANGE;
  2043.                           points[i].z = -VERYLARGE;
  2044.                           continue;
  2045.                        } else {
  2046.                           points[i].z = log10(temp);
  2047.                        }
  2048.                         } else
  2049.                        points[i].z = temp;
  2050.  
  2051.                         if (autoscale_lz
  2052.                        || inrange(temp, zmin, zmax)) {
  2053.                        points[i].type = INRANGE;
  2054.                        if (autoscale_lz) {
  2055.                           if (temp < zmin) zmin = temp;
  2056.                           if (temp > zmax) zmax = temp;
  2057.                        }
  2058.                         } else
  2059.                        points[i].type = OUTRANGE;
  2060.                     }
  2061.                     this_iso->p_count = samples;
  2062.                     this_iso = this_iso->next;
  2063.                     points = this_iso->points;
  2064.                 }
  2065.  
  2066.                 for (i = 0; i < iso_samples; i++) {
  2067.                     x = x_min + i*xisodiff;
  2068.                     /* if (log_x) PEM fix logscale x axis */
  2069.                     /* x = pow(10.0,x); 26-Sep-89 */
  2070.                     (void) complex(&plot_func.dummy_values[0],
  2071.                                 log_x ? pow(10.0,x) : x,
  2072.                                 0.0);
  2073.  
  2074.                     for (j = 0; j < samples; j++) {
  2075.                         y = y_min + j*ydiff;
  2076.                         /* if (log_y) PEM fix logscale y axis */
  2077.                         /* y = pow(10.0,y); 26-Sep-89 */
  2078.                         (void) complex(&plot_func.dummy_values[1],
  2079.                                 log_y ? pow(10.0,y) : y,
  2080.                                 0.0);
  2081.  
  2082.                         points[j].x = x;
  2083.                         points[j].y = y;
  2084.  
  2085.                         evaluate_at(plot_func.at,&a);
  2086.  
  2087.                         if (undefined || (fabs(imag(&a)) > zero)) {
  2088.                        points[j].type = UNDEFINED;
  2089.                        continue;
  2090.                         }
  2091.  
  2092.                         temp = real(&a);
  2093.  
  2094.                         if (log_z && temp < 0.0) {
  2095.                        points[j].type = UNDEFINED;
  2096.                        continue;
  2097.                         }
  2098.  
  2099.                         if (log_z) {
  2100.                        if (temp == 0.0) {
  2101.                           points[j].type = OUTRANGE;
  2102.                           points[j].z = -VERYLARGE;
  2103.                           continue;
  2104.                        } else {
  2105.                           points[j].z = log10(temp);
  2106.                        }
  2107.                         } else
  2108.                        points[j].z = temp;
  2109.  
  2110.                         if (autoscale_lz
  2111.                        || inrange(temp, zmin, zmax)) {
  2112.                        points[j].type = INRANGE;
  2113.                        if (autoscale_lz) {
  2114.                           if (temp < zmin) zmin = temp;
  2115.                           if (temp > zmax) zmax = temp;
  2116.                        }
  2117.                         } else
  2118.                        points[j].type = OUTRANGE;
  2119.                     }
  2120.                         this_iso->p_count = samples;
  2121.                     this_iso = this_iso->next;
  2122.                     points = this_iso ? this_iso->points : NULL;
  2123.                 }
  2124.              }
  2125.  
  2126.             /* title was handled above */
  2127.             if (almost_equals(c_token,"t$itle")) {
  2128.                 c_token++;
  2129.                 c_token++;
  2130.             }
  2131.  
  2132.             /* style was handled above */
  2133.             if (almost_equals(c_token,"w$ith")) {
  2134.                 c_token++;
  2135.                 c_token++;
  2136.             }
  2137.  
  2138.             /* line and point types were handled above */
  2139.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  2140.                 struct value t;
  2141.                 (void)real(const_express(&t));
  2142.             }
  2143.             if ( !equals(c_token,",") && !END_OF_COMMAND ) {
  2144.                 struct value t;
  2145.                 (void)real(const_express(&t));
  2146.             }
  2147.  
  2148.             this_plot = this_plot->next_sp;
  2149.          }
  2150.  
  2151.         if (equals(c_token,","))
  2152.           c_token++;
  2153.         else
  2154.           break;
  2155.      }
  2156.  
  2157.     if (fabs(zmax - zmin) < zero)
  2158.      /* if autoscale, widen range */
  2159.      if (autoscale_lz) {
  2160.         fprintf(stderr, "Warning: empty z range [%g:%g], ", zmin, zmax);
  2161.         if (fabs(zmin) < zero ) {
  2162.             zmin = -1.;
  2163.             zmax = 1.;
  2164.         } else {
  2165.             /* expand range by 10% in either direction */
  2166.             zmin = zmin * 0.9;
  2167.             zmax = zmax * 1.1;
  2168.         }
  2169.         fprintf(stderr, "adjusting to [%g:%g]\n", zmin, zmax);
  2170.      } else {
  2171.         int_error("z range is less than `zero`", c_token);
  2172.      }
  2173.  
  2174. /* Now we finally know the real zmin and zmax */
  2175.     if (log_z) {
  2176.         if (zmin <= 0.0 || zmax <= 0.0)
  2177.             int_error("z range must be greater than 0 for log scale!",NO_CARET);
  2178.         z_min = log10(zmin);
  2179.         z_max = log10(zmax);
  2180.     } else {
  2181.         z_min = zmin;
  2182.         z_max = zmax;
  2183.     }
  2184.     capture(replot_line,plot_token,c_token);
  2185.  
  2186.     if (parametric) {
  2187.     /* Now put u/v and x/y ranges back before we actually plot anything. */
  2188.         ltmp = autoscale_lx; autoscale_lx = autoscale_lu; autoscale_lu = ltmp;
  2189.         ltmp = autoscale_ly; autoscale_ly = autoscale_lv; autoscale_lv = ltmp;
  2190.         temp = xmin; xmin = umin; umin = temp;
  2191.         temp = xmax; xmax = umax; umax = temp;
  2192.         temp = ymin; ymin = vmin; vmin = temp;
  2193.         temp = ymax; ymax = vmax; vmax = temp;
  2194.  
  2195.     /* Now actually fix the plot triplets to be single plots. */
  2196.         parametric_3dfixup(first_3dplot, &plot_num,
  2197.                    &x_min, &x_max, &y_min, &y_max,
  2198.                    &z_min, &z_max);
  2199.         if (log_x) {
  2200.             if (x_min <= 0.0 || x_max <= 0.0)
  2201.                 int_error("x range must be greater than 0 for log scale!",NO_CARET);
  2202.             x_min = log10(x_min);
  2203.             x_max = log10(x_max);
  2204.         }
  2205.  
  2206.         if (log_y) {
  2207.             if (y_min <= 0.0 || y_max <= 0.0)
  2208.                 int_error("y range must be greater than 0 for log scale!",NO_CARET);
  2209.             y_min = log10(y_min);
  2210.             y_max = log10(y_max);
  2211.         }
  2212.  
  2213.         if (log_z) {
  2214.             if (z_min <= 0.0 || z_max <= 0.0)
  2215.                 int_error("z range must be greater than 0 for log scale!",NO_CARET);
  2216.             z_min = log10(z_min);
  2217.             z_max = log10(z_max);
  2218.         }
  2219.     }
  2220.  
  2221.  
  2222.     /* Creates contours if contours are to be plotted as well. */
  2223.     if (draw_contour) {
  2224.         for (this_plot=first_3dplot, i=0;
  2225.              i < plot_num;
  2226.              this_plot=this_plot->next_sp, i++) {
  2227.             if (this_plot->contours) {
  2228.                 struct gnuplot_contours *cntr, *cntrs = this_plot->contours;
  2229.  
  2230.                 while (cntrs) {
  2231.                     cntr = cntrs;
  2232.                     cntrs = cntrs->next;
  2233.                     free(cntr->coords);
  2234.                     free(cntr);
  2235.                 }
  2236.             }
  2237.             /* Make sure this one can be contoured. */
  2238.             if (!this_plot->has_grid_topology) {
  2239.                 this_plot->contours = NULL;
  2240.                 int_error("Can not contour non grid data!",NO_CARET);
  2241.             }
  2242.             if (this_plot->plot_type == DATA3D)
  2243.                 this_plot->contours = contour(
  2244.                            this_plot->num_iso_read,
  2245.                            this_plot->iso_crvs,
  2246.                            contour_levels, contour_pts,
  2247.                            contour_kind, contour_order);
  2248.             else
  2249.                 this_plot->contours = contour(iso_samples,
  2250.                            this_plot->iso_crvs,
  2251.                            contour_levels, contour_pts,
  2252.                            contour_kind, contour_order);
  2253.         }
  2254.     }
  2255.  
  2256.     do_3dplot(first_3dplot,plot_num,x_min,x_max,y_min,y_max,z_min,z_max);
  2257.     sp_free(first_3dplot);
  2258.     first_3dplot = NULL;
  2259. }
  2260.  
  2261. done(status)
  2262. int status;
  2263. {
  2264.     if (term && term_init)
  2265.         (*term_tbl[term].reset)();
  2266. #ifdef vms
  2267.     vms_reset();
  2268. #endif
  2269.     exit(status);
  2270. }
  2271.  
  2272. void parametric_fixup (start_plot, plot_num, x_min, x_max)
  2273. struct curve_points *start_plot;
  2274. int *plot_num;
  2275. double *x_min, *x_max;
  2276. /*
  2277.     The hardest part of this routine is collapsing the FUNC plot types
  2278.    in the list (which are gauranteed to occur in (x,y) pairs while 
  2279.     preserving the non-FUNC type plots intact.  This means we have to
  2280.     work our way through various lists.  Examples (hand checked):
  2281.         start_plot:F1->F2->NULL ==> F2->NULL
  2282.         start_plot:F1->F2->F3->F4->F5->F6->NULL ==> F2->F4->F6->NULL
  2283.         start_plot:F1->F2->D1->D2->F3->F4->D3->NULL ==> F2->D1->D2->F4->D3->NULL
  2284.     
  2285.     Of course, the more interesting work is to move the y values of
  2286.     the x function to become the x values of the y function (checking
  2287.     the mins and maxs as we go along).
  2288. */
  2289. {
  2290.     struct curve_points *xp, *new_list, *yp = start_plot, *tmp, 
  2291.             *free_list, *free_head=NULL;
  2292.     int i, tlen, curve; 
  2293.     char *new_title;
  2294.     double lxmin, lxmax, temp;
  2295.  
  2296.     if (autoscale_lx) {
  2297.         lxmin = VERYLARGE;
  2298.         lxmax = -VERYLARGE;
  2299.     } else {
  2300.         lxmin = xmin;
  2301.         lxmax = xmax;
  2302.     }
  2303.  
  2304. /* 
  2305.     Ok, go through all the plots and move FUNC types together.  Note: this
  2306.     originally was written to look for a NULL next pointer, but gnuplot 
  2307.     wants to be sticky in grabbing memory and the right number of items
  2308.     in the plot list is controlled by the plot_num variable.
  2309.  
  2310.     Since gnuplot wants to do this sticky business, a free_list of 
  2311.     curve_points is kept and then tagged onto the end of the plot list as
  2312.     this seems more in the spirit of the original memory behavior than
  2313.     simply freeing the memory.  I'm personally not convinced this sort
  2314.     of concern is worth it since the time spent computing points seems
  2315.     to dominate any garbage collecting that might be saved here...
  2316. */
  2317.     new_list = xp = start_plot; 
  2318.     yp = xp->next_cp;
  2319.    curve = 0;
  2320.     for (; curve < *plot_num; xp = xp->next_cp,yp = yp->next_cp,curve++) {
  2321.         if (xp->plot_type != FUNC) {
  2322.             continue;
  2323.         }
  2324.     /* Here's a FUNC parametric function defined as two parts. */
  2325.         --(*plot_num);
  2326.     /* 
  2327.         Go through all the points assigning the y's from xp to be the
  2328.         x's for yp.  Check max's and min's as you go.
  2329.     */
  2330.         for (i = 0; i < yp->p_count; ++i) {
  2331.         /* 
  2332.             Throw away excess xp points, mark excess yp points as OUTRANGE.
  2333.         */
  2334.             if (i > xp->p_count) {
  2335.                 yp->points[i].type = OUTRANGE;
  2336.                 continue;
  2337.             }
  2338.         /* 
  2339.             Just as we had to do when we computed y values--now check that
  2340.             x's (computed parametrically) are in the permitted ranges as well.
  2341.         */
  2342.             temp = xp->points[i].y;   /* New x value for yp function. */
  2343.             yp->points[i].x = temp;
  2344.         /* Handle undefined values differently from normal ranges. */
  2345.             if (xp->points[i].type == UNDEFINED)
  2346.                 yp->points[i].type = xp->points[i].type;  
  2347.             if (autoscale_lx || polar
  2348.                        || inrange(temp, lxmin, lxmax)) {
  2349.                if (autoscale_lx && temp < lxmin) lxmin = temp;
  2350.                 if (autoscale_lx && temp > lxmax) lxmax = temp;
  2351.             } else
  2352.             yp->points[i].type = OUTRANGE;  /* Due to x value. */
  2353.         }
  2354.    /* Ok, fix up the title to include both the xp and yp plots. */
  2355.         if (xp->title && xp->title[0] != '\0') {
  2356.             tlen = strlen (yp->title) + strlen (xp->title) + 3;
  2357.           new_title = alloc ((unsigned int) tlen, "string");
  2358.             strcpy (new_title, xp->title);  
  2359.             strcat (new_title, ", ");       /* + 2 */
  2360.             strcat (new_title, yp->title);  /* + 1 = + 3 */
  2361.             free (yp->title);
  2362.             yp->title = new_title;
  2363.         }
  2364.     /* Eliminate the first curve (xparam) and just use the second. */
  2365.         if (xp == start_plot) {
  2366.         /* Simply nip off the first element of the list. */
  2367.             new_list = first_plot = yp;
  2368.             xp = xp->next_cp;
  2369.             if (yp->next_cp != NULL)
  2370.                 yp = yp->next_cp;
  2371.         /* Add start_plot to the free_list. */
  2372.             if (free_head == NULL) {
  2373.                 free_list = free_head = start_plot;
  2374.                 free_head->next_cp = NULL;
  2375.             } else {
  2376.                 free_list->next_cp = start_plot;
  2377.                 start_plot->next_cp = NULL;
  2378.                 free_list = start_plot;
  2379.             }
  2380.         }
  2381.         else {
  2382.         /* Here, remove the xp node and replace it with the yp node. */
  2383.             tmp = xp;
  2384.         /* Pass over any data files that might have been in place. */
  2385.             while (new_list->next_cp && new_list->next_cp != xp) 
  2386.                 new_list = new_list->next_cp;
  2387.             new_list->next_cp = yp;
  2388.             new_list = new_list->next_cp;
  2389.             xp = xp->next_cp;
  2390.             if (yp->next_cp != NULL)
  2391.                 yp = yp->next_cp;
  2392.         /* Add tmp to the free_list. */
  2393.             tmp->next_cp = NULL;
  2394.             if (free_head == NULL) {
  2395.                 free_list = free_head = tmp;
  2396.             } else {
  2397.                 free_list->next_cp = tmp;
  2398.                 free_list = tmp;
  2399.             }
  2400.         }
  2401.     }
  2402. /* Ok, stick the free list at the end of the curve_points plot list. */
  2403.     while (new_list->next_cp != NULL)
  2404.         new_list = new_list->next_cp;
  2405.     new_list->next_cp = free_head;
  2406.  
  2407. /* Report the overall graph mins and maxs. */
  2408.     *x_min = lxmin;
  2409.     *x_max = lxmax;
  2410. }
  2411.  
  2412. void parametric_3dfixup(start_plot, plot_num, x_min, x_max, y_min, y_max,
  2413.                                 z_min, z_max)
  2414. struct surface_points *start_plot;
  2415. int *plot_num;
  2416. double *x_min, *x_max, *y_min, *y_max, *z_min, *z_max;
  2417. /*
  2418.     The hardest part of this routine is collapsing the FUNC plot types
  2419.    in the list (which are gauranteed to occur in (x,y,z) triplets while 
  2420.     preserving the non-FUNC type plots intact.  This means we have to
  2421.     work our way through various lists.  Examples (hand checked):
  2422.         start_plot:F1->F2->F3->NULL ==> F3->NULL
  2423.         start_plot:F1->F2->F3->F4->F5->F6->NULL ==> F3->F6->NULL
  2424.         start_plot:F1->F2->F3->D1->D2->F4->F5->F6->D3->NULL ==>
  2425.                         F3->D1->D2->F6->D3->NULL
  2426. */
  2427. {
  2428.     struct surface_points *xp, *yp, *zp, *new_list, *tmp, 
  2429.             *free_list, *free_head=NULL;
  2430.     struct iso_curve *icrvs, *xicrvs, *yicrvs, *zicrvs;
  2431.     int i, tlen, surface;
  2432.     char *new_title;
  2433.     double lxmin, lxmax, lymin, lymax, lzmin, lzmax, temp;
  2434.  
  2435.     if (autoscale_lx) {
  2436.         lxmin = VERYLARGE;
  2437.         lxmax = -VERYLARGE;
  2438.     } else {
  2439.         lxmin = xmin;
  2440.         lxmax = xmax;
  2441.     }
  2442.  
  2443.     if (autoscale_ly) {
  2444.         lymin = VERYLARGE;
  2445.         lymax = -VERYLARGE;
  2446.     } else {
  2447.         lymin = ymin;
  2448.         lymax = ymax;
  2449.     }
  2450.  
  2451.     if (autoscale_lz) {
  2452.         lzmin = VERYLARGE;
  2453.         lzmax = -VERYLARGE;
  2454.     } else {
  2455.         lzmin = zmin;
  2456.         lzmax = zmax;
  2457.     }
  2458.  
  2459. /* 
  2460.     Ok, go through all the plots and move FUNC3D types together.  Note:
  2461.     this originally was written to look for a NULL next pointer, but
  2462.     gnuplot wants to be sticky in grabbing memory and the right number
  2463.     of items in the plot list is controlled by the plot_num variable.
  2464.  
  2465.     Since gnuplot wants to do this sticky business, a free_list of 
  2466.     surface_points is kept and then tagged onto the end of the plot list as
  2467.     this seems more in the spirit of the original memory behavior than
  2468.     simply freeing the memory.  I'm personally not convinced this sort
  2469.     of concern is worth it since the time spent computing points seems
  2470.     to dominate any garbage collecting that might be saved here...
  2471. */
  2472.     new_list = xp = start_plot; 
  2473.     for (surface = 0; surface < *plot_num; surface++) {
  2474.         if (xp->plot_type != FUNC3D) {
  2475.             icrvs = xp->iso_crvs;
  2476.  
  2477.             while ( icrvs ) {
  2478.                 struct coordinate *points = icrvs->points;
  2479.  
  2480.                 for (i = 0; i < icrvs->p_count; ++i) {
  2481.                     if (lxmin > points[i].x)
  2482.                         lxmin = points[i].x;
  2483.                     if (lxmax < points[i].x)
  2484.                         lxmax = points[i].x;
  2485.                     if (lymin > points[i].y)
  2486.                         lymin = points[i].y;
  2487.                     if (lymax < points[i].y)
  2488.                         lymax = points[i].y;
  2489.                     if (lzmin > points[i].z)
  2490.                         lzmin = points[i].z;
  2491.                     if (lzmax < points[i].z)
  2492.                         lzmax = points[i].z;
  2493.                 }
  2494.  
  2495.                 icrvs = icrvs->next;
  2496.             }
  2497.             xp = xp->next_sp;
  2498.             continue;
  2499.         }
  2500.  
  2501.         yp = xp->next_sp;
  2502.         zp = yp->next_sp;
  2503.  
  2504.     /* Here's a FUNC3D parametric function defined as three parts. */
  2505.         (*plot_num) -= 2;
  2506.     /* 
  2507.         Go through all the points and assign the x's and y's from xp
  2508.         and yp to zp.  Check max's and min's as you go.
  2509.     */
  2510.         xicrvs = xp->iso_crvs;
  2511.         yicrvs = yp->iso_crvs;
  2512.         zicrvs = zp->iso_crvs;
  2513.         while ( zicrvs ) {
  2514.             struct coordinate *xpoints = xicrvs->points,
  2515.                      *ypoints = yicrvs->points,
  2516.                      *zpoints = zicrvs->points;
  2517.             for (i = 0; i < zicrvs->p_count; ++i) {
  2518.                 zpoints[i].x = xpoints[i].z;
  2519.                 zpoints[i].y = ypoints[i].z;
  2520.  
  2521.                 if (lxmin > zpoints[i].x) lxmin = zpoints[i].x;
  2522.                 if (lxmax < zpoints[i].x) lxmax = zpoints[i].x;
  2523.                 if (lymin > zpoints[i].y) lymin = zpoints[i].y;
  2524.                 if (lymax < zpoints[i].y) lymax = zpoints[i].y;
  2525.                 if (lzmin > zpoints[i].z) lzmin = zpoints[i].z;
  2526.                 if (lzmax < zpoints[i].z) lzmax = zpoints[i].z;
  2527.             }
  2528.             xicrvs = xicrvs->next;
  2529.             yicrvs = yicrvs->next;
  2530.             zicrvs = zicrvs->next;
  2531.         }
  2532.  
  2533.     /* Ok, fix up the title to include xp and yp plots. */
  2534.         if ((xp->title && xp->title[0] != '\0') ||
  2535.             (yp->title && yp->title[0] != '\0')) {
  2536.             tlen = (xp->title ? strlen(xp->title) : 0) +
  2537.                    (yp->title ? strlen(yp->title) : 0) +
  2538.                    (zp->title ? strlen(zp->title) : 0) + 5;
  2539.             new_title = alloc ((unsigned int) tlen, "string");
  2540.             new_title[0] = 0;
  2541.             if (xp->title) {
  2542.                 strcat(new_title, xp->title);
  2543.                 strcat(new_title, ", ");       /* + 2 */
  2544.             }
  2545.             if (yp->title) {
  2546.                 strcat(new_title, yp->title);
  2547.                 strcat(new_title, ", ");       /* + 2 */
  2548.             }
  2549.             if (zp->title) {
  2550.                 strcat(new_title, zp->title);
  2551.             }
  2552.             free (zp->title);
  2553.             zp->title = new_title;
  2554.         }
  2555.  
  2556.     /* Eliminate the first two surfaces (xp and yp) and just use the third. */
  2557.         if (xp == start_plot) {
  2558.         /* Simply nip off the first two elements of the list. */
  2559.             new_list = first_3dplot = zp;
  2560.             xp = zp->next_sp;
  2561.         /* Add xp and yp to the free_list. */
  2562.             if (free_head == NULL) {
  2563.                 free_head = start_plot;
  2564.             } else {
  2565.                 free_list->next_sp = start_plot;
  2566.             }
  2567.             free_list = start_plot->next_sp;
  2568.             free_list->next_sp = NULL;
  2569.         }
  2570.         else {
  2571.         /* Here, remove the xp,yp nodes and replace them with the zp node. */
  2572.             tmp = xp;
  2573.         /* Pass over any data files that might have been in place. */
  2574.             while (new_list->next_sp && new_list->next_sp != xp)
  2575.                 new_list = new_list->next_sp;
  2576.             new_list->next_sp = zp;
  2577.             new_list = zp;
  2578.             xp = zp->next_sp;
  2579.         /* Add tmp to the free_list. */
  2580.             if (free_head == NULL) {
  2581.                 free_head = tmp;
  2582.             } else {
  2583.                 free_list->next_sp = tmp;
  2584.             }
  2585.             free_list = tmp->next_sp;
  2586.             free_list->next_sp = NULL;
  2587.         }
  2588.     }
  2589. /* Ok, stick the free list at the end of the surface_points plot list. */
  2590.     while (new_list->next_sp != NULL)
  2591.         new_list = new_list->next_sp;
  2592.     new_list->next_sp = free_head;
  2593.  
  2594. /* Report the overall graph mins and maxs. */
  2595.     if (autoscale_lx) {
  2596.         *x_min = (log_x ? pow(10.0, lxmin) : lxmin);
  2597.         *x_max = (log_x ? pow(10.0, lxmax) : lxmax);
  2598.     }
  2599.     else {
  2600.         *x_min = xmin;
  2601.         *x_max = xmax;
  2602.     }
  2603.     if (autoscale_ly) {
  2604.         *y_min = (log_y ? pow(10.0, lymin) : lymin);
  2605.         *y_max = (log_y ? pow(10.0, lymax) : lymax);
  2606.     }
  2607.     else {
  2608.         *y_min = ymin;
  2609.         *y_max = ymax;
  2610.     }
  2611.     if (autoscale_lz) {
  2612.         *z_min = (log_z ? pow(10.0, lzmin) : lzmin);
  2613.         *z_max = (log_z ? pow(10.0, lzmax) : lzmax);
  2614.     }
  2615.     else {
  2616.         *z_min = zmin;
  2617.         *z_max = zmax;
  2618.     }
  2619. }
  2620.  
  2621. #ifdef AMIGA_LC_5_1
  2622. void sleep(delay)
  2623. unsigned int delay;
  2624. {
  2625.   Delay(50 * delay);
  2626. }
  2627. #endif
  2628.  
  2629. #ifdef AMIGA_AC_5
  2630. void sleep(delay)
  2631. unsigned int delay;
  2632. {
  2633. unsigned long time_is_up;
  2634.     time_is_up = time(NULL) + (unsigned long) delay; 
  2635.     while (time(NULL)<time_is_up)
  2636.         /* wait */ ;
  2637. }
  2638. #endif
  2639.  
  2640. #ifdef MSDOS
  2641. #ifndef __TURBOC__    /* Turbo C already has sleep() */
  2642. #ifndef __ZTC__     /* ZTC already has usleep() */
  2643. /* kludge to provide sleep() for msc 5.1 */
  2644. void sleep(delay)
  2645. unsigned int delay;
  2646. {
  2647. unsigned long time_is_up;
  2648.     time_is_up = time(NULL) + (unsigned long) delay; 
  2649.     while (time(NULL)<time_is_up)
  2650.         /* wait */ ;
  2651. }
  2652. #endif /* not ZTC */
  2653. #endif /* not TURBOC */
  2654. #endif /* MSDOS */
  2655.  
  2656.  
  2657. /* Support for input, shell, and help for various systems */
  2658.  
  2659. #ifdef vms
  2660.  
  2661. #include <descrip.h>
  2662. #include <rmsdef.h>
  2663. #include <errno.h>
  2664. #include <smgdef.h>
  2665. #include <smgmsg.h>
  2666.  
  2667. extern lib$get_input(), lib$put_output();
  2668. extern smg$read_composed_line();
  2669.  
  2670. int vms_len;
  2671.  
  2672. unsigned int status[2] = {1, 0};
  2673.  
  2674. static char help[MAX_LINE_LEN+1] = "gnuplot";
  2675.  
  2676. $DESCRIPTOR(prompt_desc,PROMPT);
  2677. $DESCRIPTOR(line_desc,input_line);
  2678.  
  2679. $DESCRIPTOR(help_desc,help);
  2680. $DESCRIPTOR(helpfile_desc,"GNUPLOT$HELP");
  2681.  
  2682.  
  2683. read_line(prompt)
  2684. char *prompt;
  2685. {
  2686.     int more, start=0;
  2687.     char expand_prompt[40];
  2688.  
  2689.     prompt_desc.dsc$w_length = strlen (prompt);
  2690.     prompt_desc.dsc$a_pointer = prompt;
  2691.     (void) strcpy (expand_prompt, "_");
  2692.     (void) strncat (expand_prompt, prompt, 38);
  2693.     do {
  2694.         line_desc.dsc$w_length = MAX_LINE_LEN - start;
  2695.         line_desc.dsc$a_pointer = &input_line[start];
  2696.         switch(status[1] = smg$read_composed_line(&vms_vkid,0,&line_desc, &prompt_desc, &vms_len)){
  2697.           case SMG$_EOF:
  2698.           done(IO_SUCCESS);    /* ^Z isn't really an error */
  2699.           break;
  2700.           case RMS$_TNS:    /* didn't press return in time *
  2701.                            /
  2702.                            vms_len--; /* skip the last character */
  2703.           break;            /* and parse anyway */
  2704.           case RMS$_BES:    /* Bad Escape Sequence */
  2705.           case RMS$_PES:    /* Partial Escape Sequence */
  2706.           sys$putmsg(status);
  2707.           vms_len = 0;        /* ignore the line */
  2708.           break;
  2709.           case SS$_NORMAL:
  2710.           break;            /* everything's fine */
  2711.           default:
  2712.           done(status[1]);    /* give the error message */
  2713.         }
  2714.         start += vms_len;
  2715.         input_line[start] = '\0';
  2716.        inline_num++;
  2717.         if (input_line[start-1] == '\\') {
  2718.           /* Allow for a continuation line. */
  2719.           prompt_desc.dsc$w_length = strlen (expand_prompt);
  2720.           prompt_desc.dsc$a_pointer = expand_prompt;
  2721.           more = 1;
  2722.           --start;
  2723.         }
  2724.         else {
  2725.           line_desc.dsc$w_length = strlen(input_line);
  2726.           line_desc.dsc$a_pointer = input_line;
  2727.           more = 0;
  2728.         }
  2729.     } while (more);
  2730. }
  2731.  
  2732.  
  2733. do_help()
  2734. {
  2735.     help_desc.dsc$w_length = strlen(help);
  2736.     if ((vaxc$errno = lbr$output_help(lib$put_output,0,&help_desc,
  2737.         &helpfile_desc,0,lib$get_input)) != SS$_NORMAL)
  2738.             os_error("can't open GNUPLOT$HELP");
  2739. }
  2740.  
  2741.  
  2742. do_shell()
  2743. {
  2744.     if ((vaxc$errno = lib$spawn()) != SS$_NORMAL) {
  2745.         os_error("spawn error",NO_CARET);
  2746.     }
  2747. }
  2748.  
  2749.  
  2750. do_system()
  2751. {
  2752.     input_line[0] = ' ';    /* an embarrassment, but... */
  2753.  
  2754.     if ((vaxc$errno = lib$spawn(&line_desc)) != SS$_NORMAL)
  2755.         os_error("spawn error",NO_CARET);
  2756.  
  2757.     (void) putc('\n',stderr);
  2758. }
  2759.  
  2760. #else /* vms */
  2761.  
  2762. /* do_help: (not VMS, although it would work)
  2763.  * Give help to the user. 
  2764.  * It parses the command line into helpbuf and supplies help for that 
  2765.  * string. Then, if there are subtopics available for that key,
  2766.  * it prompts the user with this string. If more input is
  2767.  * given, do_help is called recursively, with the argument the index of 
  2768.  * null character in the string. Thus a more specific help can be 
  2769.  * supplied. This can be done repeatedly. 
  2770.  * If null input is given, the function returns, effecting a
  2771.  * backward climb up the tree.
  2772.  * David Kotz (David.Kotz@Dartmouth.edu) 10/89
  2773.  */
  2774. do_help()
  2775. {
  2776.     static char *helpbuf = NULL;
  2777.     static char *prompt = NULL;
  2778.     int base;                /* index of first char AFTER help string */
  2779.     int len;                /* length of current help string */
  2780.     BOOLEAN more_help;
  2781.     BOOLEAN only;            /* TRUE if only printing subtopics */
  2782.     int subtopics;            /* 0 if no subtopics for this topic */
  2783.     int start;                /* starting token of help string */
  2784.     char *help_ptr;            /* name of help file */
  2785.  
  2786.     if ( (help_ptr = getenv("GNUHELP")) == (char *)NULL )
  2787.         /* if can't find environment variable then just use HELPFILE */
  2788.         help_ptr = HELPFILE;
  2789.  
  2790.     /* Since MSDOS DGROUP segment is being overflowed we can not allow such  */
  2791.     /* huge static variables (1k each). Instead we dynamically allocate them */
  2792.     /* on the first call to this function...                     */
  2793.     if (helpbuf == NULL) {
  2794.     helpbuf = alloc(MAX_LINE_LEN, "help buffer");
  2795.     prompt = alloc(MAX_LINE_LEN, "help prompt");
  2796.     helpbuf[0] = prompt[0] = 0;
  2797.     }
  2798.  
  2799.     len = base = strlen(helpbuf);
  2800.  
  2801.     /* find the end of the help command */
  2802.     for (start = c_token; !(END_OF_COMMAND); c_token++)
  2803.      ;
  2804.     /* copy new help input into helpbuf */
  2805.     if (len > 0)
  2806.      helpbuf[len++] = ' ';    /* add a space */
  2807.     capture(helpbuf+len, start, c_token-1);
  2808.     squash_spaces(helpbuf+base); /* only bother with new stuff */
  2809.     lower_case(helpbuf+base); /* only bother with new stuff */
  2810.     len = strlen(helpbuf);
  2811.  
  2812.     /* now, a lone ? will print subtopics only */
  2813.     if (strcmp(helpbuf + (base ? base+1 : 0), "?") == 0) {
  2814.        /* subtopics only */
  2815.        subtopics = 1;
  2816.        only = TRUE;
  2817.        helpbuf[base] = '\0';    /* cut off question mark */
  2818.     } else {
  2819.        /* normal help request */
  2820.        subtopics = 0;
  2821.        only = FALSE;
  2822.     }
  2823.  
  2824.     switch (help(helpbuf, help_ptr, &subtopics)) {
  2825.        case H_FOUND: {
  2826.           /* already printed the help info */
  2827.           /* subtopics now is true if there were any subtopics */
  2828.           screen_ok = FALSE;
  2829.     
  2830.           do {
  2831.              if (subtopics && !only) {
  2832.                 /* prompt for subtopic with current help string */
  2833.                 if (len > 0)
  2834.                   (void) sprintf(prompt, "Subtopic of %s: ", helpbuf);
  2835.                 else
  2836.                   (void) strcpy(prompt, "Help topic: ");
  2837.                 read_line(prompt);
  2838.                 num_tokens = scanner(input_line);
  2839.                 c_token = 0;
  2840.                 more_help = !(END_OF_COMMAND);
  2841.                 if (more_help)
  2842.                   /* base for next level is all of current helpbuf */
  2843.                   do_help();
  2844.              } else 
  2845.                more_help = FALSE;
  2846.           } while(more_help);
  2847.     
  2848.           break;
  2849.        }
  2850.        case H_NOTFOUND: {
  2851.           printf("Sorry, no help for '%s'\n", helpbuf);
  2852.           break;
  2853.        }
  2854.        case H_ERROR: {
  2855.           perror(help_ptr);
  2856.           break;
  2857.        }
  2858.        default: {        /* defensive programming */
  2859.           int_error("Impossible case in switch\n", NO_CARET);
  2860.           /* NOTREACHED */
  2861.        }
  2862.     }
  2863.     
  2864.     helpbuf[base] = '\0';    /* cut it off where we started */
  2865. }
  2866.  
  2867. #ifdef AMIGA_AC_5
  2868. char strg0[256];
  2869. #endif
  2870.  
  2871. do_system()
  2872. {
  2873. #ifdef AMIGA_AC_5
  2874.    char *parms[80];
  2875.    void getparms();
  2876.  
  2877.    getparms(input_line+1,parms);
  2878.    if(fexecv(parms[0],parms) < 0)
  2879. #else
  2880.    if (system(input_line + 1))
  2881. #endif
  2882.       os_error("system() failed",NO_CARET);
  2883. }
  2884.  
  2885. #ifdef AMIGA_AC_5
  2886.  
  2887. /******************************************************************************/
  2888. /*                                                                            */
  2889. /*  Parses the command string (for fexecv use) and  converts the first token  */
  2890. /*     to lower case                                                          */
  2891. /*                                                                            */
  2892. /******************************************************************************/
  2893.  
  2894. void getparms(command,parms)
  2895.    char *command;
  2896.    char **parms;
  2897.    {
  2898.    register int i = 0;                         /* A bunch of indices          */
  2899.    register int j = 0;
  2900.    register int k = 0;
  2901.  
  2902.    while(*(command+j) != '\0')                 /* Loop on string characters   */
  2903.       {
  2904.       parms[k++] = strg0+i;
  2905.       while(*(command+j) == ' ') ++j;
  2906.       while(*(command+j) != ' ' && *(command+j) != '\0')
  2907.          {
  2908.          if(*(command+j) == '"')               /* Get quoted string           */
  2909.             for(*(strg0+(i++)) = *(command+(j++));
  2910.                 *(command+j)  != '"';
  2911.                 *(strg0+(i++)) = *(command+(j++)));
  2912.          *(strg0+(i++)) = *(command+(j++));
  2913.          }
  2914.       *(strg0+(i++)) = '\0';                   /* NUL terminate every token   */
  2915.       }
  2916.    parms[k] = '\0';
  2917.  
  2918.    for(k=strlen(strg0)-1; k>=0; --k)           /* Convert to lower case       */
  2919.       *(strg0+k)>='A' && *(strg0+k)<='Z'? *(strg0+k)|=32: *(strg0+k);
  2920.    }
  2921.  
  2922. #endif /* AMIGA_AC_5 */
  2923.  
  2924. #ifdef READLINE
  2925. char *
  2926. rlgets(s, n, prompt)
  2927. char *s;
  2928. int n;
  2929. char *prompt;
  2930. {
  2931.       char *readline();
  2932.       static char *line = (char *)NULL;
  2933.  
  2934.       /* If we already have a line, first free it */
  2935.       if(line != (char *)NULL) 
  2936.               free(line);
  2937.  
  2938.       line = readline((interactive)?prompt:"");
  2939.  
  2940.       /* If it's not an EOF */
  2941.       if(line) {
  2942.       if (*line)
  2943.               add_history(line);
  2944.       strncpy(s, line, n);
  2945.       return s;
  2946.       }
  2947.  
  2948.       return line;
  2949. }
  2950. #endif /* READLINE */
  2951.  
  2952. #ifdef MSDOS
  2953.  
  2954. #ifdef __TURBOC__
  2955. /* cgets implemented using dos functions */
  2956. /* Maurice Castro 22/5/91 */
  2957. char *doscgets(s)
  2958. char *s;
  2959. {
  2960.    long datseg;
  2961.  
  2962.    /* protect and preserve segments - call dos to do the dirty work */
  2963.    datseg = _DS;
  2964.  
  2965.    _DX = FP_OFF(s);
  2966.    _DS = FP_SEG(s);
  2967.    _AH = 0x0A;
  2968.    geninterrupt(33);
  2969.    _DS = datseg;
  2970.  
  2971.    /* check for a carriage return and then clobber it with a null */
  2972.    if (s[s[1]+2] == '\r') 
  2973.       s[s[1]+2] = 0;
  2974.  
  2975.    /* return the input string */
  2976.    return(&(s[2]));
  2977.    }
  2978. #endif /* __TURBOC__ */
  2979.  
  2980.  
  2981. read_line(prompt)
  2982.     char *prompt;
  2983. {
  2984.     register int i;
  2985.     int start = 0, ilen = 0;
  2986.     BOOLEAN more;
  2987.     int last;
  2988.     char *p, *crnt_prompt = prompt;
  2989.     
  2990. #ifndef __ZTC__
  2991.     if (interactive) { /* if interactive use console IO so CED will work */
  2992. #ifndef READLINE
  2993.         cputs(prompt);
  2994. #endif /* READLINE */
  2995.         do {
  2996.            ilen = MAX_LINE_LEN-start-1;
  2997.            input_line[start] = ilen > 126 ? 126 : ilen;
  2998. #ifdef READLINE
  2999.            input_line[start+2] = 0;
  3000.            (void) rlgets(&(input_line[start+2]), ilen, crnt_prompt );
  3001.            if (p = strchr(&(input_line[start+2]), '\r')) *p = 0;
  3002.            if (p = strchr(&(input_line[start+2]), '\n')) *p = 0;
  3003.            input_line[start+1] = strlen(&(input_line[start+2]));
  3004. #else /* READLINE */
  3005. #ifdef __TURBOC__
  3006.            (void) doscgets(&(input_line[start]));
  3007. #else /* __TURBOC__ */
  3008.            (void) cgets(&(input_line[start]));
  3009. #endif /* __TURBOC__ */
  3010.            (void) putc('\n',stderr);
  3011. #endif /* READLINE */
  3012.            if (input_line[start+2] == 26) {
  3013.               /* end-of-file */
  3014.               (void) putc('\n',stderr);
  3015.               input_line[start] = '\0';
  3016.               inline_num++;
  3017.               if (start > 0)    /* don't quit yet - process what we have */
  3018.                 more = FALSE;
  3019.               else {
  3020.                  (void) putc('\n',stderr);
  3021.                  done(IO_SUCCESS);
  3022.                  /* NOTREACHED */
  3023.               }
  3024.            } else {
  3025.               /* normal line input */
  3026.               register i = start;
  3027.               while ( (input_line[i] = input_line[i+2]) != (char)NULL )
  3028.                 i++;        /* yuck!  move everything down two characters */
  3029.  
  3030.               inline_num++;
  3031.               last = strlen(input_line) - 1;
  3032.               if (last + 1 >= MAX_LINE_LEN)
  3033.                 int_error("Input line too long",NO_CARET);
  3034.                      
  3035.               if (input_line[last] == '\\') { /* line continuation */
  3036.                  start = last;
  3037.                  more = TRUE;
  3038.               } else
  3039.                 more = FALSE;
  3040.            }
  3041. #ifndef READLINE
  3042.            if (more)
  3043.             cputs("> ");
  3044. #else
  3045.            crnt_prompt = "> ";
  3046. #endif /* READLINE */
  3047.         } while(more);
  3048.     }
  3049.     else { /* not interactive */
  3050. #endif /* not ZTC */
  3051.         if (interactive)
  3052.          fputs(prompt,stderr);
  3053.         do {
  3054.            /* grab some input */
  3055.            if ( fgets(&(input_line[start]), MAX_LINE_LEN - start, stdin) 
  3056.                     == (char *)NULL ) {
  3057.               /* end-of-file */
  3058.               if (interactive)
  3059.                 (void) putc('\n',stderr);
  3060.               input_line[start] = '\0';
  3061.               inline_num++;
  3062.               if (start > 0)    /* don't quit yet - process what we have */
  3063.                 more = FALSE;
  3064.               else
  3065.                 done(IO_SUCCESS); /* no return */
  3066.            } else {
  3067.               /* normal line input */
  3068.               last = strlen(input_line) - 1;
  3069.               if (input_line[last] == '\n') { /* remove any newline */
  3070.                  input_line[last] = '\0';
  3071.                  /* Watch out that we don't backup beyond 0 (1-1-1) */
  3072.                  if (last > 0) --last;
  3073.                  inline_num++;
  3074.               } else if (last+1 >= MAX_LINE_LEN)
  3075.                 int_error("Input line too long",NO_CARET);
  3076.                      
  3077.               if (input_line[last] == '\\') { /* line continuation */
  3078.                  start = last;
  3079.                  more = TRUE;
  3080.               } else
  3081.                 more = FALSE;
  3082.            }
  3083.             if (more && interactive)
  3084.             fputs("> ", stderr);
  3085.         } while(more);
  3086. #ifndef __ZTC
  3087.     }
  3088. #endif
  3089. }
  3090.  
  3091.  
  3092. do_shell()
  3093. {
  3094. register char *comspec;
  3095.     if ((comspec = getenv("COMSPEC")) == (char *)NULL)
  3096.         comspec = "\command.com";
  3097.     if (spawnl(P_WAIT,comspec,NULL) == -1)
  3098.         os_error("unable to spawn shell",NO_CARET);
  3099. }
  3100.  
  3101. #else /* MSDOS */
  3102.         /* plain old Unix */
  3103.  
  3104. read_line(prompt)
  3105.     char *prompt;
  3106. {
  3107.     int start = 0;
  3108.     BOOLEAN more = FALSE;
  3109.     int last = 0;
  3110.  
  3111. #ifndef READLINE
  3112.     if (interactive)
  3113.      fputs(prompt,stderr);
  3114. #endif /* READLINE */
  3115.     do {
  3116.        /* grab some input */
  3117. #ifdef READLINE
  3118.      if (((interactive)
  3119.          ?rlgets(&(input_line[start]), MAX_LINE_LEN - start,
  3120.                 ((more)?"> ":prompt))
  3121.          :fgets(&(input_line[start]), MAX_LINE_LEN - start, stdin))
  3122.                               == (char *)NULL ) {
  3123. #else
  3124.        if ( fgets(&(input_line[start]), MAX_LINE_LEN - start, stdin) 
  3125.                 == (char *)NULL ) {
  3126. #endif /* READLINE */
  3127.           /* end-of-file */
  3128.           if (interactive)
  3129.             (void) putc('\n',stderr);
  3130.           input_line[start] = '\0';
  3131.           inline_num++;
  3132.           if (start > 0)    /* don't quit yet - process what we have */
  3133.             more = FALSE;
  3134.           else
  3135.             done(IO_SUCCESS); /* no return */
  3136.        } else {
  3137.           /* normal line input */
  3138.           last = strlen(input_line) - 1;
  3139.           if (input_line[last] == '\n') { /* remove any newline */
  3140.              input_line[last] = '\0';
  3141.                 /* Watch out that we don't backup beyond 0 (1-1-1) */
  3142.              if (last > 0) --last;
  3143.              inline_num++;
  3144.           } else if (last+1 >= MAX_LINE_LEN)
  3145.             int_error("Input line too long",NO_CARET);
  3146.                  
  3147.           if (input_line[last] == '\\') { /* line continuation */
  3148.              start = last;
  3149.              more = TRUE;
  3150.           } else
  3151.             more = FALSE;
  3152.        }
  3153. #ifndef READLINE
  3154.         if (more && interactive)
  3155.         fputs("> ", stderr);
  3156. #endif
  3157.     } while(more);
  3158. }
  3159.  
  3160. #ifdef VFORK
  3161.  
  3162. do_shell()
  3163. {
  3164. register char *shell;
  3165. register int p;
  3166. static int execstat;
  3167.     if (!(shell = getenv("SHELL")))
  3168.         shell = SHELL;
  3169. #ifdef AMIGA_AC_5
  3170.     execstat = fexecl(shell,shell,NULL);
  3171. #else
  3172.     if ((p = vfork()) == 0) {
  3173.         execstat = execl(shell,shell,NULL);
  3174.         _exit(1);
  3175.     } else if (p == -1)
  3176.         os_error("vfork failed",c_token);
  3177.     else
  3178.         while (wait(NULL) != p)
  3179. #endif
  3180.             ;
  3181.     if (execstat == -1)
  3182.         os_error("shell exec failed",c_token);
  3183.     (void) putc('\n',stderr);
  3184. }
  3185. #else /* VFORK */
  3186.  
  3187. #ifdef AMIGA_LC_5_1
  3188. do_shell()
  3189. {
  3190. register char *shell;
  3191.     if (!(shell = getenv("SHELL")))
  3192.         shell = SHELL;
  3193.  
  3194.     if (system(shell))
  3195.         os_error("system() failed",NO_CARET);
  3196.  
  3197.     (void) putc('\n',stderr);
  3198. }
  3199. #else /* AMIGA_LC_5_1 */
  3200.  
  3201. #define EXEC "exec "
  3202. do_shell()
  3203. {
  3204. static char exec[100] = EXEC;
  3205. register char *shell;
  3206.     if (!(shell = getenv("SHELL")))
  3207.         shell = SHELL;
  3208.  
  3209.     if (system(strncpy(&exec[sizeof(EXEC)-1],shell,
  3210.         sizeof(exec)-sizeof(EXEC)-1)))
  3211.         os_error("system() failed",NO_CARET);
  3212.  
  3213.     (void) putc('\n',stderr);
  3214. }
  3215. #endif /* AMIGA_LC_5_1 */
  3216. #endif /* VFORK */
  3217. #endif /* MSDOS */
  3218. #endif /* vms */
  3219.